diff --git a/.gitattributes b/.gitattributes index 76eac516..01d20f30 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,6 @@ .gitkeep export-ignore docs/speeches export-ignore docs/video export-ignore +docs/src/man7 export-ignore +bin/cdist-build-helper export-ignore +README-maintainers export-ignore diff --git a/.gitignore b/.gitignore index 0c664232..85a8ccc7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,22 @@ # -vim -.*.swp +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim + +# Temporary +.netrwhist +*~ +*.tmp +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ # Ignore generated manpages docs/src/.marker @@ -7,6 +24,8 @@ docs/src/man1/*.1 docs/src/man7/*.7 docs/src/man7/cdist-type__*.rst docs/src/cdist-reference.rst +docs/src/cdist-types.rst +docs/src/cdist.cfg.skeleton # Ignore cdist cache for version control /cache/ @@ -17,7 +36,7 @@ cdist/inventory/ # Python: cache, distutils, distribution in general __pycache__/ *.pyc -MANIFEST +/MANIFEST dist/ cdist/version.py cdist.egg-info/ @@ -27,6 +46,7 @@ _build/ docs/dist # Ignore temp files used for signing +cdist-*.tar cdist-*.tar.gz cdist-*.tar.gz.asc diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..a4bc67aa --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,23 @@ +--- +image: code.ungleich.ch:5050/ungleich-public/cdist/cdist-ci:latest + +stages: + - test + +before_script: + - ./bin/cdist-build-helper version + +shellcheck: + stage: test + script: + - ./bin/cdist-build-helper shellcheck + +pycodestyle: + stage: test + script: + - ./bin/cdist-build-helper pycodestyle + +unit_tests: + stage: test + script: + - ./bin/cdist-build-helper test diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..14682ad6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + cdist + Copyright (C) 2019 ungleich-public + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + cdist Copyright (C) 2019 ungleich-public + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Makefile b/Makefile index d727bccc..3712511c 100644 --- a/Makefile +++ b/Makefile @@ -18,31 +18,30 @@ # # -helper=./bin/build-helper +.PHONY: help +help: + @echo "Please use \`make ' where is one of" + @echo "man build only man user documentation" + @echo "html build only html user documentation" + @echo "docs build both man and html user documentation" + @echo "dotman build man pages for types in your ~/.cdist directory" + @echo "speeches build speeches pdf files" + @echo "install install in the system site-packages directory" + @echo "install-user install in the user site-packages directory" + @echo "docs-clean clean documentation" + @echo "clean clean" -DOCS_SRC_DIR=docs/src -SPEECHDIR=docs/speeches -TYPEDIR=cdist/conf/type - -WEBSRCDIR=docs/web - -WEBDIR=$$HOME/vcs/www.nico.schottelius.org -WEBBLOG=$(WEBDIR)/blog -WEBBASE=$(WEBDIR)/software/cdist -WEBPAGE=$(WEBBASE).mdwn - -CHANGELOG_VERSION=$(shell $(helper) changelog-version) -CHANGELOG_FILE=docs/changelog - -PYTHON_VERSION=cdist/version.py +DOCS_SRC_DIR=./docs/src +SPEECHDIR=./docs/speeches +TYPEDIR=./cdist/conf/type SPHINXM=make -C $(DOCS_SRC_DIR) man SPHINXH=make -C $(DOCS_SRC_DIR) html SPHINXC=make -C $(DOCS_SRC_DIR) clean + ################################################################################ # Manpages # -MAN1DSTDIR=$(DOCS_SRC_DIR)/man1 MAN7DSTDIR=$(DOCS_SRC_DIR)/man7 # Manpages #1: Types @@ -54,6 +53,7 @@ MANTYPES=$(subst /man.rst,.rst,$(MANTYPEPREFIX)) # Link manpage: do not create man.html but correct named file $(MAN7DSTDIR)/cdist-type%.rst: $(TYPEDIR)/%/man.rst + mkdir -p $(MAN7DSTDIR) ln -sf "../../../$^" $@ # Manpages #2: reference @@ -63,11 +63,28 @@ DOCSREFSH=$(DOCS_SRC_DIR)/cdist-reference.rst.sh $(DOCSREF): $(DOCSREFSH) $(DOCSREFSH) +# Html types list with references +DOCSTYPESREF=$(MAN7DSTDIR)/cdist-types.rst +DOCSTYPESREFSH=$(DOCS_SRC_DIR)/cdist-types.rst.sh + +$(DOCSTYPESREF): $(DOCSTYPESREFSH) + $(DOCSTYPESREFSH) + +DOCSCFGSKEL=./configuration/cdist.cfg.skeleton + +configskel: $(DOCSCFGSKEL) + cp -f "$(DOCSCFGSKEL)" "$(DOCS_SRC_DIR)/" + +version: + @[ -f "cdist/version.py" ] || { \ + printf "Missing 'cdist/version.py', please generate it first.\n" && exit 1; \ + } + # Manpages #3: generic part -man: $(MANTYPES) $(DOCSREF) $(PYTHON_VERSION) +man: version configskel $(MANTYPES) $(DOCSREF) $(DOCSTYPESREF) $(SPHINXM) -html: $(MANTYPES) $(DOCSREF) $(PYTHON_VERSION) +html: version configskel $(MANTYPES) $(DOCSREF) $(DOCSTYPESREF) $(SPHINXH) docs: man html @@ -75,24 +92,6 @@ docs: man html docs-clean: $(SPHINXC) -# Manpages #5: release part -MANWEBDIR=$(WEBBASE)/man/$(CHANGELOG_VERSION) -HTMLBUILDDIR=docs/dist/html - -docs-dist: html - rm -rf "${MANWEBDIR}" - mkdir -p "${MANWEBDIR}" - # mkdir -p "${MANWEBDIR}/man1" "${MANWEBDIR}/man7" - # cp ${MAN1DSTDIR}/*.html ${MAN1DSTDIR}/*.css ${MANWEBDIR}/man1 - # cp ${MAN7DSTDIR}/*.html ${MAN7DSTDIR}/*.css ${MANWEBDIR}/man7 - cp -R ${HTMLBUILDDIR}/* ${MANWEBDIR} - cd ${MANWEBDIR} && git add . && git commit -m "cdist manpages update: $(CHANGELOG_VERSION)" || true - -man-latest-link: web-pub - # Fix ikiwiki, which does not like symlinks for pseudo security - ssh staticweb.ungleich.ch \ - "cd /home/services/www/nico/nico.schottelius.org/www/software/cdist/man/ && rm -f latest && ln -sf "$(CHANGELOG_VERSION)" latest" - # Manpages: .cdist Types DOT_CDIST_PATH=${HOME}/.cdist DOTMAN7DSTDIR=$(MAN7DSTDIR) @@ -105,8 +104,7 @@ DOTMANTYPES=$(subst /man.rst,.rst,$(DOTMANTYPEPREFIX)) $(DOTMAN7DSTDIR)/cdist-type%.rst: $(DOTTYPEDIR)/%/man.rst ln -sf "$^" $@ -# Manpages #3: generic part -dotman: $(DOTMANTYPES) +dotman: version configskel $(DOTMANTYPES) $(DOCSREF) $(DOCSTYPESREF) $(SPHINXM) ################################################################################ @@ -114,7 +112,6 @@ dotman: $(DOTMANTYPES) # SPEECHESOURCES=$(SPEECHDIR)/*.tex SPEECHES=$(SPEECHESOURCES:.tex=.pdf) -SPEECHESWEBDIR=$(WEBBASE)/speeches # Create speeches and ensure Toc is up-to-date $(SPEECHDIR)/%.pdf: $(SPEECHDIR)/%.tex @@ -124,131 +121,28 @@ $(SPEECHDIR)/%.pdf: $(SPEECHDIR)/%.tex speeches: $(SPEECHES) -speeches-dist: speeches - rm -rf "${SPEECHESWEBDIR}" - mkdir -p "${SPEECHESWEBDIR}" - cp ${SPEECHES} "${SPEECHESWEBDIR}" - cd ${SPEECHESWEBDIR} && git add . && git commit -m "cdist speeches updated" || true - ################################################################################ -# Website +# Misc # - -BLOGFILE=$(WEBBLOG)/cdist-$(CHANGELOG_VERSION)-released.mdwn - -$(BLOGFILE): $(CHANGELOG_FILE) - $(helper) blog $(CHANGELOG_VERSION) $(BLOGFILE) - -web-blog: $(BLOGFILE) - -web-doc: - # Go to top level, because of cdist.mdwn - rsync -av "$(WEBSRCDIR)/" "${WEBBASE}/.." - cd "${WEBBASE}/.." && git add cdist* && git commit -m "cdist doc update" cdist* || true - -web-dist: web-blog web-doc - -web-pub: web-dist docs-dist speeches-dist - cd "${WEBDIR}" && make pub - -web-release-all: man-latest-link -web-release-all-no-latest: web-pub - -################################################################################ -# Release: Mailinglist -# -ML_FILE=.lock-ml - -# Only send mail once - lock until new changelog things happened -$(ML_FILE): $(CHANGELOG_FILE) - $(helper) ml-release $(CHANGELOG_VERSION) - touch $@ - -ml-release: $(ML_FILE) - - -################################################################################ -# pypi -# -PYPI_FILE=.pypi-release -$(PYPI_FILE): man $(PYTHON_VERSION) - python3 setup.py sdist upload - touch $@ - -pypi-release: $(PYPI_FILE) -################################################################################ -# archlinux -# -ARCHLINUX_FILE=.lock-archlinux -ARCHLINUXTAR=cdist-$(CHANGELOG_VERSION)-1.src.tar.gz - -$(ARCHLINUXTAR): PKGBUILD - umask 022; mkaurball - -PKGBUILD: PKGBUILD.in $(PYTHON_VERSION) - ./PKGBUILD.in $(CHANGELOG_VERSION) - -$(ARCHLINUX_FILE): $(ARCHLINUXTAR) $(PYTHON_VERSION) - burp -c system $(ARCHLINUXTAR) - touch $@ - -archlinux-release: $(ARCHLINUX_FILE) - -################################################################################ -# Release -# - -$(PYTHON_VERSION) version: .git/refs/heads/master - $(helper) version - -# Code that is better handled in a shell script -check-%: - $(helper) $@ - -release: - $(helper) $@ - -################################################################################ -# Cleanup -# - -clean: +clean: docs-clean rm -f $(DOCS_SRC_DIR)/cdist-reference.rst + rm -f $(DOCS_SRC_DIR)/cdist-types.rst + rm -f $(DOCS_SRC_DIR)/cdist.cfg.skeleton find "$(DOCS_SRC_DIR)" -mindepth 2 -type l \ | xargs rm -f - make -C $(DOCS_SRC_DIR) clean - find * -name __pycache__ | xargs rm -rf - # Archlinux - rm -f cdist-*.pkg.tar.xz cdist-*.tar.gz - rm -rf pkg/ src/ - - rm -f MANIFEST PKGBUILD - rm -rf dist/ - - # Signed release - rm -f cdist-*.tar.gz - rm -f cdist-*.tar.gz.asc - -distclean: clean - rm -f cdist/version.py + # distutils + rm -rf ./build ################################################################################ -# Misc +# install # -# The pub is Nico's "push to all git remotes" way ("make pub") -pub: - git push --mirror +install: + python3 setup.py install -test: - $(helper) $@ - -test-remote: - $(helper) $@ - -pep8: - $(helper) $@ +install-user: + python3 setup.py install --user diff --git a/PKGBUILD.in b/PKGBUILD.in index c967249d..c0188e68 100755 --- a/PKGBUILD.in +++ b/PKGBUILD.in @@ -9,7 +9,7 @@ pkgver=$version pkgrel=1 pkgdesc='A Usable Configuration Management System"' arch=('any') -url='http://www.nico.schottelius.org/software/cdist/' +url='https://www.cdi.st/' license=('GPL3') depends=('python>=3.2.0') source=("http://pypi.python.org/packages/source/c/cdist/cdist-\${pkgver}.tar.gz") diff --git a/README b/README deleted file mode 100644 index a67e25e3..00000000 --- a/README +++ /dev/null @@ -1,6 +0,0 @@ -cdist ------ - -cdist is a usable configuration management system. - -For the web documentation have a look at docs/web/. diff --git a/README-maintainers b/README-maintainers new file mode 100644 index 00000000..5766dd7d --- /dev/null +++ b/README-maintainers @@ -0,0 +1,4 @@ +Maintainers should use ./bin/cdist-build-helper script. + +Makefile is intended for end users. It can be used for non-maintaining +targets that can be run from pure source (without git repository). diff --git a/README.md b/README.md new file mode 100644 index 00000000..de6901c7 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# cdist + +**cdist** is a usable configuration management system. + +It adheres to the [**KISS principle**](https://en.wikipedia.org/wiki/KISS_principle) +and is being used in small up to enterprise grade environments. + +For more information have a look at [**homepage**](https://cdi.st) +or at **``docs/src``** for manual in **reStructuredText** format. + +## Contributing + +Merge/Pull requests can be made in both +[upstream **GitLab**](https://code.ungleich.ch/ungleich-public/cdist/merge_requests) +(managed by [**ungleich**](https://ungleich.ch)) +and [**GitHub** project](https://github.com/ungleich/cdist/pulls). + +Issues can be made and other project management activites happen +[**only in GitLab**](https://code.ungleich.ch/ungleich-public/cdist) +(needs [**ungleich** account](https://account.ungleich.ch)). + +For community-maintained types there is +[**cdist-contrib** project](https://code.ungleich.ch/ungleich-public/cdist-contrib). + +## Participating + +IRC: ``#cdist`` @ freenode + +Matrix: ``#cdist:ungleich.ch`` + +Mattermost: https://chat.ungleich.ch/ungleich/channels/cdist diff --git a/bin/build-helper b/bin/build-helper deleted file mode 100755 index ef15b6c2..00000000 --- a/bin/build-helper +++ /dev/null @@ -1,411 +0,0 @@ -#!/bin/sh -# -# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org) -# -# This file is part of cdist. -# -# cdist is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# cdist is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with cdist. If not, see . -# -# -# This file contains the heavy lifting found usually in the Makefile -# - -basedir=${0%/*}/../ -# Change to checkout directory -cd "$basedir" - -version=$(git describe) - -option=$1; shift - -case "$option" in - changelog-changes) - if [ "$#" -eq 1 ]; then - start=$1 - else - start="[[:digit:]]" - fi - - end="[[:digit:]]" - - awk -F: "BEGIN { start=0 } - { - if(start == 0) { - if (\$0 ~ /^$start/) { - start = 1 - } - } else { - if (\$0 ~ /^$end/) { - exit - } else { - print \$0 - } - } - }" "$basedir/docs/changelog" - ;; - - changelog-version) - # get version from changelog - grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/:.*//' - ;; - - check-date) - # verify date in changelog is today - date_today="$(date +%Y-%m-%d)" - date_changelog=$(grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/.*: //') - - if [ "$date_today" != "$date_changelog" ]; then - echo "Date in changelog is not today" - echo "Changelog: $date_changelog" - exit 1 - fi - ;; - - check-unittest) - "$0" test - ;; - - blog) - version=$1; shift - blogfile=$1; shift - dir=${blogfile%/*} - file=${blogfile##*/} - - - cat << eof > "$blogfile" -[[!meta title="Cdist $version released"]] - -Here's a short overview about the changes found in version ${version}: - -eof - - $0 changelog-changes "$version" >> "$blogfile" - - cat << eof >> "$blogfile" -For more information visit the [[cdist homepage|software/cdist]]. - -[[!tag cdist config unix]] -eof - cd "$dir" - git add "$file" - # Allow git commit to fail if there are no changes - git commit -m "cdist blog update: $version" "$blogfile" || true - ;; - - ml-release) - if [ $# -ne 1 ]; then - echo "$0 ml-release version" >&2 - exit 1 - fi - - version=$1; shift - - to_a=cdist - to_d=l.schottelius.org - to=${to_a}@${to_d} - - from_a=nico-cdist - from_d=schottelius.org - from=${from_a}@${from_d} - - ( - cat << eof -From: Nico -telmich- Schottelius <$from> -To: cdist mailing list <$to> -Subject: cdist $version released - -Hello .*, - -cdist $version has been released with the following changes: - -eof - - "$0" changelog-changes "$version" - cat << eof - -Cheers, - -Nico - --- -Automatisation at its best level. With cdist. -eof - ) | /usr/sbin/sendmail -f "$from" "$to" - ;; - - - release-git-tag) - target_version=$($0 changelog-version) - if git rev-parse --verify refs/tags/$target_version 2>/dev/null; then - echo "Tag for $target_version exists, aborting" - exit 1 - fi - printf "Enter tag description for ${target_version}: " - read tagmessage - - # setup for signed tags: - # gpg --fulL-gen-key - # gpg --list-secret-keys --keyid-format LONG - # git config --local user.signingkey - # for exporting pub key: - # gpg --armor --export > pubkey.asc - # gpg --output pubkey.gpg --export - # show tag with signature - # git show - # verify tag signature - # git tag -v - # - # gpg verify signature - # gpg --verify - # gpg --no-default-keyring --keyring --verify - # Ensure gpg-agent is running. - export GPG_TTY=$(tty) - gpg-agent - - git tag -s "$target_version" -m "$tagmessage" - git push --tags - ;; - - sign-git-release) - if [ $# -lt 2 ] - then - printf "usage: $0 sign-git-release TAG TOKEN [ARCHIVE]\n" - printf " if ARCHIVE is not specified then it is created\n" - exit 1 - fi - tag="$1" - if ! git rev-parse -q --verify "${tag}" >/dev/null 2>&1 - then - printf "Tag \"${tag}\" not found.\n" - exit 1 - fi - token="$2" - if [ $# -gt 2 ] - then - archivename="$3" - else - archivename="cdist-${tag}.tar.gz" - git archive --prefix="cdist-${tag}/" -o "${archivename}" "${tag}" \ - || exit 1 - fi - gpg --armor --detach-sign "${archivename}" || exit 1 - - # make github release - curl -H "Authorization: token ${token}" \ - --request POST \ - --data "{ \"tag_name\":\"${tag}\", \ - \"target_commitish\":\"master\", \ - \"name\": \"${tag}\", \ - \"body\":\"${tag}\", \ - \"draft\":false, \ - \"prerelease\": false}" \ - "https://api.github.com/repos/ungleich/cdist/releases" || exit 1 - - # get release ID - repoid=$(curl "https://api.github.com/repos/ungleich/cdist/releases/tags/${tag}" \ - | python3 -c 'import json; import sys; print(json.loads(sys.stdin.read())["id"])') \ - || exit 1 - - # upload archive and then signature - curl -H "Authorization: token ${token}" \ - -H "Accept: application/vnd.github.manifold-preview" \ - -H "Content-Type: application/x-gtar" \ - --data-binary @${archivename} \ - "https://uploads.github.com/repos/ungleich/cdist/releases/${repoid}/assets?name=${archivename}" \ - || exit 1 - curl -H "Authorization: token ${token}" \ - -H "Accept: application/vnd.github.manifold-preview" \ - -H "Content-Type: application/pgp-signature" \ - --data-binary @${archivename}.asc \ - "https://uploads.github.com/repos/ungleich/cdist/releases/${repoid}/assets?name=${archivename}.asc" \ - || exit 1 - - # remove generated files (archive and asc) - if [ $# -eq 2] - then - rm -f "${archivename}" - fi - rm -f "${archivename}.asc" - ;; - - release) - set -e - target_version=$($0 changelog-version) - target_branch=$($0 version-branch) - - echo "Beginning release process for $target_version" - - # First check everything is sane - "$0" check-date - "$0" check-unittest - "$0" check-pep8 - - # Generate version file to be included in packaging - "$0" target-version - - # Ensure the git status is clean, else abort - if ! git diff-index --name-only --exit-code HEAD ; then - echo "Unclean tree, see files above, aborting" - exit 1 - fi - - # Ensure we are on the master branch - masterbranch=yes - if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then - echo "Releases are happening from the master branch, aborting" - - echo "Enter the magic word to release anyway" - read magicword - - if [ "$magicword" = "iknowwhatido" ]; then - masterbranch=no - else - exit 1 - fi - fi - - if [ "$masterbranch" = yes ]; then - # Ensure version branch exists - if ! git rev-parse --verify refs/heads/$target_branch 2>/dev/null; then - git branch "$target_branch" - fi - - # Merge master branch into version branch - git checkout "$target_branch" - git merge master - fi - - # Verify that after the merge everything works - "$0" check-date - "$0" check-unittest - - # Generate documentation (man and html) - # First, clean old generated docs - make docs-clean - make docs - - # Generate speeches (indirect check if they build) - make speeches - - ############################################################# - # Everything green, let's do the release - - # Tag the current commit - "$0" release-git-tag - - # sign git tag - printf "Enter github authentication token: " - read token - "$0" sign-git-release "${target_version}" "${token}" - - # Also merge back the version branch - if [ "$masterbranch" = yes ]; then - git checkout master - git merge "$target_branch" - fi - - # Publish git changes - make pub - - # publish man, speeches, website - if [ "$masterbranch" = yes ]; then - make web-release-all - else - make web-release-all-no-latest - fi - - # Ensure that pypi release has the right version - "$0" version - - # Create and publish package for pypi - make pypi-release - - # Archlinux release is based on pypi - make archlinux-release - - # Announce change on ML - make ml-release - - cat << eof -Manual steps post release: - - - linkedin - - hackernews - - reddit - - twitter - -eof - - ;; - - test) - export PYTHONPATH="$(pwd -P)" - - if [ $# -lt 1 ]; then - python3 -m cdist.test - else - python3 -m unittest "$@" - fi - ;; - - test-remote) - export PYTHONPATH="$(pwd -P)" - python3 -m cdist.test.exec.remote - ;; - - pep8) - pep8 "${basedir}" "${basedir}/scripts/cdist" | less - ;; - - check-pep8) - "$0" pep8 - echo "Please review pep8 report." - while true - do - echo "Continue (yes/no)?" - any= - read any - case "$any" in - yes) - break - ;; - no) - exit 1 - ;; - *) - echo "Please answer with 'yes' or 'no' explicitly." - ;; - esac - done - ;; - - version-branch) - "$0" changelog-version | cut -d. -f '1,2' - ;; - - version) - echo "VERSION = \"$(git describe)\"" > cdist/version.py - ;; - - target-version) - target_version=$($0 changelog-version) - echo "VERSION = \"${target_version}\"" > cdist/version.py - ;; - - *) - echo "Unknown helper target $@ - aborting" - exit 1 - ;; - -esac diff --git a/bin/build-helper.freebsd b/bin/build-helper.freebsd deleted file mode 100755 index e49b2f7a..00000000 --- a/bin/build-helper.freebsd +++ /dev/null @@ -1,473 +0,0 @@ -#!/bin/sh -# -# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org) -# 2016 Darko Poljak (darko.poljak at gmail.com) -# -# This file is part of cdist. -# -# cdist is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# cdist is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with cdist. If not, see . -# -# -# This file contains the heavy lifting found usually in the Makefile -# - -# vars for make -helper=$0 - -basedir=${0%/*}/../ -# run_as is used to check how the script is called (by $0 value) -# currently supported sufixes for $0 are: -# .freebsd - run as freebsd -basename=${0##*/} -run_as=${basename#*.} -case "$run_as" in - freebsd) - to_a=cdist-configuration-management - to_d=googlegroups.com - from_a=darko.poljak - from_d=gmail.com - ml_name="Darko Poljak" - ml_sig_name="Darko" - - # vars for make - WEBDIR=../vcs/www.nico.schottelius.org - ;; - *) - to_a=cdist - to_d=l.schottelius.org - from_a=nico-cdist - from_d=schottelius.org - ml_name="Nico -telmich- Schottelius" - ml_sig_name="Nico" - - # vars for make - WEBDIR=$$HOME/vcs/www.nico.schottelius.org - ;; -esac - -# Change to checkout directory -cd "$basedir" - -version=$(git describe) - -option=$1; shift - -case "$option" in - print-make-vars) - printf "helper: ${helper}\n" - printf "WEBDIR: ${WEBDIR}\n" - ;; - print-runas) - printf "run_as: $run_as\n" - ;; - changelog-changes) - if [ "$#" -eq 1 ]; then - start=$1 - else - start="[[:digit:]]" - fi - - end="[[:digit:]]" - - awk -F: "BEGIN { start=0 } - { - if(start == 0) { - if (\$0 ~ /^$start/) { - start = 1 - } - } else { - if (\$0 ~ /^$end/) { - exit - } else { - print \$0 - } - } - }" "$basedir/docs/changelog" - ;; - - changelog-version) - # get version from changelog - grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/:.*//' - ;; - - check-date) - # verify date in changelog is today - date_today="$(date +%Y-%m-%d)" - date_changelog=$(grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/.*: //') - - if [ "$date_today" != "$date_changelog" ]; then - echo "Date in changelog is not today" - echo "Changelog: $date_changelog" - exit 1 - fi - ;; - - check-unittest) - "$0" test - ;; - - blog) - version=$1; shift - blogfile=$1; shift - dir=${blogfile%/*} - file=${blogfile##*/} - - - cat << eof > "$blogfile" -[[!meta title="Cdist $version released"]] - -Here's a short overview about the changes found in version ${version}: - -eof - - $0 changelog-changes "$version" >> "$blogfile" - - cat << eof >> "$blogfile" -For more information visit the [[cdist homepage|software/cdist]]. - -[[!tag cdist config unix]] -eof - cd "$dir" - git add "$file" - # Allow git commit to fail if there are no changes - git commit -m "cdist blog update: $version" "$blogfile" || true - ;; - - ml-release) - if [ $# -ne 1 ]; then - echo "$0 ml-release version" >&2 - exit 1 - fi - - version=$1; shift - - to=${to_a}@${to_d} - from=${from_a}@${from_d} - - ( - cat << eof -From: ${ml_name} <$from> -To: cdist mailing list <$to> -Subject: cdist $version released - -Hello .*, - -cdist $version has been released with the following changes: - -eof - - "$0" changelog-changes "$version" - cat << eof - -Cheers, - -${ml_sig_name} - --- -Automatisation at its best level. With cdist. -eof - ) | /usr/sbin/sendmail -f "$from" "$to" - ;; - - release-git-tag) - target_version=$($0 changelog-version) - if git rev-parse --verify refs/tags/$target_version 2>/dev/null; then - echo "Tag for $target_version exists, aborting" - exit 1 - fi - printf "Enter tag description for ${target_version}: " - read tagmessage - - # setup for signed tags: - # gpg --fulL-gen-key - # gpg --list-secret-keys --keyid-format LONG - # git config --local user.signingkey - # for exporting pub key: - # gpg --armor --export > pubkey.asc - # gpg --output pubkey.gpg --export - # show tag with signature - # git show - # verify tag signature - # git tag -v - # - # gpg verify signature - # gpg --verify - # gpg --no-default-keyring --keyring --verify - # Ensure gpg-agent is running. - export GPG_TTY=$(tty) - gpg-agent - - git tag -s "$target_version" -m "$tagmessage" - git push --tags - ;; - - sign-git-release) - if [ $# -lt 2 ] - then - printf "usage: $0 sign-git-release TAG TOKEN [ARCHIVE]\n" - printf " if ARCHIVE is not specified then it is created\n" - exit 1 - fi - tag="$1" - if ! git rev-parse -q --verify "${tag}" >/dev/null 2>&1 - then - printf "Tag \"${tag}\" not found.\n" - exit 1 - fi - token="$2" - if [ $# -gt 2 ] - then - archivename="$3" - else - archivename="cdist-${tag}.tar.gz" - git archive --prefix="cdist-${tag}/" -o "${archivename}" "${tag}" \ - || exit 1 - fi - gpg --armor --detach-sign "${archivename}" || exit 1 - - # make github release - curl -H "Authorization: token ${token}" \ - --request POST \ - --data "{ \"tag_name\":\"${tag}\", \ - \"target_commitish\":\"master\", \ - \"name\": \"${tag}\", \ - \"body\":\"${tag}\", \ - \"draft\":false, \ - \"prerelease\": false}" \ - "https://api.github.com/repos/ungleich/cdist/releases" || exit 1 - - # get release ID - repoid=$(curl "https://api.github.com/repos/ungleich/cdist/releases/tags/${tag}" \ - | python3 -c 'import json; import sys; print(json.loads(sys.stdin.read())["id"])') \ - || exit 1 - - # upload archive and then signature - curl -H "Authorization: token ${token}" \ - -H "Accept: application/vnd.github.manifold-preview" \ - -H "Content-Type: application/x-gtar" \ - --data-binary @${archivename} \ - "https://uploads.github.com/repos/ungleich/cdist/releases/${repoid}/assets?name=${archivename}" \ - || exit 1 - curl -H "Authorization: token ${token}" \ - -H "Accept: application/vnd.github.manifold-preview" \ - -H "Content-Type: application/pgp-signature" \ - --data-binary @${archivename}.asc \ - "https://uploads.github.com/repos/ungleich/cdist/releases/${repoid}/assets?name=${archivename}.asc" \ - || exit 1 - - # remove generated files (archive and asc) - if [ $# -eq 2] - then - rm -f "${archivename}" - fi - rm -f "${archivename}.asc" - ;; - - release) - set -e - target_version=$($0 changelog-version) - target_branch=$($0 version-branch) - - echo "Beginning release process for $target_version" - - # First check everything is sane - "$0" check-date - "$0" check-unittest - "$0" check-pep8 - - # Generate version file to be included in packaging - "$0" target-version - - # Ensure the git status is clean, else abort - if ! git diff-index --name-only --exit-code HEAD ; then - echo "Unclean tree, see files above, aborting" - exit 1 - fi - - # Ensure we are on the master branch - masterbranch=yes - if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then - echo "Releases are happening from the master branch, aborting" - - echo "Enter the magic word to release anyway" - read magicword - - if [ "$magicword" = "iknowwhatido" ]; then - masterbranch=no - else - exit 1 - fi - fi - - if [ "$masterbranch" = yes ]; then - # Ensure version branch exists - if ! git rev-parse --verify refs/heads/$target_branch 2>/dev/null; then - git branch "$target_branch" - fi - - # Merge master branch into version branch - git checkout "$target_branch" - git merge master - fi - - # Verify that after the merge everything works - "$0" check-date - "$0" check-unittest - - # Generate documentation (man and html) - # First, clean old generated docs - make helper=${helper} WEBDIR=${WEBDIR} docs-clean - make helper=${helper} WEBDIR=${WEBDIR} docs - - # Generate speeches (indirect check if they build) - make helper=${helper} WEBDIR=${WEBDIR} speeches - - ############################################################# - # Everything green, let's do the release - - # Tag the current commit - "$0" release-git-tag - - # sign git tag - printf "Enter github authentication token: " - read token - "$0" sign-git-release "${target_version}" "${token}" - - # Also merge back the version branch - if [ "$masterbranch" = yes ]; then - git checkout master - git merge "$target_branch" - fi - - # Publish git changes - case "$run_as" in - freebsd) - # if we are not Nico :) then just push, no mirror - git push - # push also new branch and set up tracking - git push -u origin "${target_branch}" - ;; - *) - make helper=${helper} WEBDIR=${WEBDIR} pub - ;; - esac - - # publish man, speeches, website - if [ "$masterbranch" = yes ]; then - make helper=${helper} WEBDIR=${WEBDIR} web-release-all - else - make helper=${helper} WEBDIR=${WEBDIR} web-release-all-no-latest - fi - - # Ensure that pypi release has the right version - "$0" version - - # Create and publish package for pypi - make helper=${helper} WEBDIR=${WEBDIR} pypi-release - - case "$run_as" in - freebsd) - ;; - *) - # Archlinux release is based on pypi - make archlinux-release - ;; - esac - - # Announce change on ML - make helper=${helper} WEBDIR=${WEBDIR} ml-release - - cat << eof -Manual steps post release: - - - linkedin - - hackernews - - reddit - - twitter - -eof - - case "$run_as" in - freebsd) - cat < cdist/version.py - ;; - - target-version) - target_version=$($0 changelog-version) - echo "VERSION = \"${target_version}\"" > cdist/version.py - ;; - - *) - echo "Unknown helper target $@ - aborting" - exit 1 - ;; - -esac diff --git a/bin/cdist b/bin/cdist index 645020a1..ddaffa7f 100755 --- a/bin/cdist +++ b/bin/cdist @@ -1,7 +1,8 @@ -#!/bin/sh +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# 2012 Nico Schottelius (nico-cdist at schottelius.org) +# 2010-2016 Nico Schottelius (nico-cdist at schottelius.org) +# 2016 Darko Poljak (darko.poljak at gmail.com) # # This file is part of cdist. # @@ -20,14 +21,81 @@ # # -# Wrapper for real script to allow execution from checkout -dir=${0%/*} +import logging +import os +import sys -# Ensure version is present - the bundled/shipped version contains a static version, -# the git version contains a dynamic version -"$dir/build-helper" version +# See if this file's parent is cdist module +# and if so add it to module search path. +cdist_dir = os.path.realpath( + os.path.join( + os.path.dirname(os.path.realpath(__file__)), + os.pardir)) +cdist_init_dir = os.path.join(cdist_dir, 'cdist', '__init__.py') +if os.path.exists(cdist_init_dir): + sys.path.insert(0, cdist_dir) -libdir=$(cd "${dir}/../" && pwd -P) -export PYTHONPATH="${libdir}" +import cdist # noqa 402 +import cdist.argparse # noqa 402 +import cdist.banner # noqa 402 +import cdist.config # noqa 402 +import cdist.install # noqa 402 +import cdist.shell # noqa 402 +import cdist.inventory # noqa 402 -"$dir/../scripts/cdist" "$@" + +def commandline(): + """Parse command line""" + + # preos subcommand hack + if len(sys.argv) > 1 and sys.argv[1] == 'preos': + return cdist.preos.PreOS.commandline(sys.argv[1:]) + parser, cfg = cdist.argparse.parse_and_configure(sys.argv[1:]) + args = cfg.get_args() + + # Work around python 3.3 bug: + # http://bugs.python.org/issue16308 + # http://bugs.python.org/issue9253 + + # FIXME: catching AttributeError also hides + # real problems.. try a different way + + # FIXME: we always print main help, not + # the help of the actual parser being used! + try: + getattr(args, "func") + except AttributeError: + parser['main'].print_help() + sys.exit(0) + + args.func(args) + + +if __name__ == "__main__": + if sys.version < cdist.MIN_SUPPORTED_PYTHON_VERSION: + print('Python >= {} is required on the source host.'.format( + cdist.MIN_SUPPORTED_PYTHON_VERSIO), file=sys.stderr) + sys.exit(1) + + exit_code = 0 + + try: + import re + import os + + if re.match("__", os.path.basename(sys.argv[0])): + import cdist.emulator + emulator = cdist.emulator.Emulator(sys.argv) + emulator.run() + else: + commandline() + + except KeyboardInterrupt: + exit_code = 2 + + except cdist.Error as e: + log = logging.getLogger("cdist") + log.error(e) + exit_code = 1 + + sys.exit(exit_code) diff --git a/bin/cdist-build-helper b/bin/cdist-build-helper new file mode 100755 index 00000000..0380b3f8 --- /dev/null +++ b/bin/cdist-build-helper @@ -0,0 +1,574 @@ +#!/bin/sh +# +# 2011-2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2016-2019 Darko Poljak (darko.poljak at gmail.com) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# This file contains the heavy lifting found usually in the Makefile. +# + +usage() { + printf "usage: %s TARGET [TARGET-ARGS...] + Available targets: + changelog-changes + changelog-version + check-date + check-unittest + ml-release + archlinux-release + pypi-release + release-git-tag + sign-git-release + release + test + test-remote + pycodestyle + pep8 + check-pycodestyle + shellcheck-global-explorers + shellcheck-type-explorers + shellcheck-manifests + shellcheck-local-gencodes + shellcheck-remote-gencodes + shellcheck-bin + shellcheck-gencodes + shellcheck-types + shellcheck + shellcheck-type-files + shellcheck-with-files + shellcheck-build-helper + check-shellcheck + version-branch + version + target-version + clean + distclean\n" "$1" +} + +basename="${0##*/}" + +if [ $# -lt 1 ] +then + usage "${basename}" + exit 1 +fi + +option=$1; shift + +SHELLCHECKCMD="shellcheck -s sh -f gcc -x" +# Skip SC2154 for variables starting with __ since such variables are cdist +# environment variables. +SHELLCHECK_SKIP=': __.*is referenced but not assigned.*\[SC2154\]' +SHELLCHECKTMP=".shellcheck.tmp" + +# Change to checkout directory +basedir="${0%/*}/../" +cd "$basedir" + +case "$option" in + changelog-changes) + if [ "$#" -eq 1 ]; then + start=$1 + else + start="[[:digit:]]" + fi + + end="[[:digit:]]" + + awk -F: "BEGIN { start=0 } + { + if(start == 0) { + if (\$0 ~ /^$start/) { + start = 1 + } + } else { + if (\$0 ~ /^$end/) { + exit + } else { + print \$0 + } + } + }" "$basedir/docs/changelog" + ;; + + changelog-version) + # get version from changelog + grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/:.*//' + ;; + + check-date) + # verify date in changelog is today + date_today="$(date +%Y-%m-%d)" + date_changelog=$(grep '^[[:digit:]]' "$basedir/docs/changelog" | head -n1 | sed 's/.*: //') + + if [ "$date_today" != "$date_changelog" ]; then + printf "Date in changelog is not today\n" + printf "Changelog date: %s\n" "${date_changelog}" + exit 1 + fi + ;; + + check-unittest) + "$0" test + ;; + + ml-release) + if [ $# -ne 1 ]; then + printf "%s ml-release version\n" "$0" >&2 + exit 1 + fi + + version=$1; shift + + ( + cat << eof +Subject: cdist $version has been released + +Hello .*, + +cdist $version has been released with the following changes: + +eof + + "$0" changelog-changes "$version" + cat << eof + +eof + ) > mailinglist.tmp + ;; + + archlinux-release) + if [ $# -ne 1 ]; then + printf "%s archlinux-release version\n" "$0" >&2 + exit 1 + fi + version=$1; shift + + ARCHLINUXTAR="cdist-${version}-1.src.tar.gz" + ./PKGBUILD.in "${version}" + umask 022 + mkaurball + burp -c system "${ARCHLINUXTAR}" + ;; + + pypi-release) + # Ensure that pypi release has the right version + "$0" version + + make docs-clean + make docs + python3 setup.py sdist upload + ;; + + release-git-tag) + target_version=$($0 changelog-version) + if git rev-parse --verify "refs/tags/${target_version}" 2>/dev/null; then + printf "Tag for %s exists, aborting\n" "${target_version}" + exit 1 + fi + printf "Enter tag description for %s: " "${target_version}" + read -r tagmessage + + # setup for signed tags: + # gpg --fulL-gen-key + # gpg --list-secret-keys --keyid-format LONG + # git config --local user.signingkey + # for exporting pub key: + # gpg --armor --export > pubkey.asc + # gpg --output pubkey.gpg --export + # show tag with signature + # git show + # verify tag signature + # git tag -v + # + # gpg verify signature + # gpg --verify + # gpg --no-default-keyring --keyring --verify + # Ensure gpg-agent is running. + GPG_TTY=$(tty) + export GPG_TTY + gpg-agent + + git tag -s "$target_version" -m "$tagmessage" + git push --tags + ;; + + sign-git-release) + if [ $# -lt 2 ] + then + printf "usage: %s sign-git-release TAG TOKEN [ARCHIVE]\n" "$0" + printf " if ARCHIVE is not specified then it is created\n" + exit 1 + fi + tag="$1" + if ! git rev-parse -q --verify "${tag}" >/dev/null 2>&1 + then + printf "Tag \"%s\" not found.\n" "${tag}" + exit 1 + fi + token="$2" + if [ $# -gt 2 ] + then + archivename="$3" + else + archivename="cdist-${tag}.tar" + git archive --prefix="cdist-${tag}/" -o "${archivename}" "${tag}" \ + || exit 1 + # make sure target version is generated + "$0" target-version + tar -x -f "${archivename}" || exit 1 + cp cdist/version.py "cdist-${tag}/cdist/version.py" || exit 1 + tar -c -f "${archivename}" "cdist-${tag}/" || exit 1 + rm -r -f "cdist-${tag}/" + gzip "${archivename}" || exit 1 + archivename="${archivename}.gz" + fi + gpg --armor --detach-sign "${archivename}" || exit 1 + + project="ungleich-public%2Fcdist" + sed_cmd='s/^.*"markdown":"\([^"]*\)".*$/\1/' + + # upload archive + response_archive=$(curl -f -X POST \ + --http1.1 \ + -H "PRIVATE-TOKEN: ${token}" \ + -F "file=@${archivename}" \ + "https://code.ungleich.ch/api/v4/projects/${project}/uploads" \ + | sed "${sed_cmd}") || exit 1 + + # upload archive signature + response_archive_sig=$(curl -f -X POST \ + --http1.1 \ + -H "PRIVATE-TOKEN: ${token}" \ + -F "file=@${archivename}.asc" \ + "https://code.ungleich.ch/api/v4/projects/${project}/uploads" \ + | sed "${sed_cmd}") || exit 1 + + # make release + changelog=$("$0" changelog-changes "$1" | sed 's/^[[:space:]]*//') + release_notes=$( + printf "%s\n\n%s\n\n**Changelog**\n\n%s\n" \ + "${response_archive}" "${response_archive_sig}" "${changelog}" + ) + curl -f -X POST \ + -H "PRIVATE-TOKEN: ${token}" \ + -F "description=${release_notes}" \ + "https://code.ungleich.ch/api/v4/projects/${project}/repository/tags/${tag}/release" \ + || exit 1 + + # remove generated files (archive and asc) + if [ $# -eq 2 ] + then + rm -f "${archivename}" + fi + rm -f "${archivename}.asc" + ;; + + release) + set -e + target_version=$($0 changelog-version) + target_branch=$($0 version-branch) + + printf "Beginning release process for %s\n" "${target_version}" + + # First check everything is sane + "$0" check-date + "$0" check-unittest + "$0" check-pycodestyle + "$0" check-shellcheck + + # Generate version file to be included in packaging + "$0" target-version + + # Ensure the git status is clean, else abort + if ! git diff-index --name-only --exit-code HEAD ; then + printf "Unclean tree, see files above, aborting.\n" + exit 1 + fi + + # Ensure we are on the master branch + masterbranch=yes + if [ "$(git rev-parse --abbrev-ref HEAD)" != "master" ]; then + printf "Releases are happening from the master branch, aborting.\n" + + printf "Enter the magic word to release anyway:" + read -r magicword + + if [ "$magicword" = "iknowwhatido" ]; then + masterbranch=no + else + exit 1 + fi + fi + + if [ "$masterbranch" = yes ]; then + # Ensure version branch exists + if ! git rev-parse --verify "refs/heads/${target_branch}" 2>/dev/null; then + git branch "$target_branch" + fi + + # Merge master branch into version branch + git checkout "$target_branch" + git merge master + fi + + # Verify that after the merge everything works + "$0" check-date + "$0" check-unittest + + # Generate documentation (man and html) + # First, clean old generated docs + make docs-clean + make docs + + ############################################################# + # Everything green, let's do the release + + # Tag the current commit + "$0" release-git-tag + + # Also merge back the version branch + if [ "$masterbranch" = yes ]; then + git checkout master + git merge "$target_branch" + fi + + # Publish git changes + # if you want to have mirror locally then uncomment this and comment below + # git push --mirror + git push + # push also new branch and set up tracking + git push -u origin "${target_branch}" + # fi + + # Create and publish package for pypi + "$0" pypi-release + + # sign git tag + printf "Enter upstream repository authentication token: " + read -r token + "$0" sign-git-release "${target_version}" "${token}" + + # Announce change on ML + "$0" ml-release "${target_version}" + + cat << eof +Manual steps post release: + - cdist-web + - send generated mailinglist.tmp mail +eof + ;; + + test) + if [ ! -f "cdist/version.py" ] + then + printf "cdist/version.py is missing, generate it first.\n" + exit 1 + fi + + PYTHONPATH="$(pwd -P)" + export PYTHONPATH + + if [ $# -lt 1 ]; then + python3 -m cdist.test + else + python3 -m unittest "$@" + fi + ;; + + test-remote) + if [ ! -f "cdist/version.py" ] + then + printf "cdist/version.py is missing, generate it first.\n" + exit 1 + fi + + PYTHONPATH="$(pwd -P)" + export PYTHONPATH + + python3 -m cdist.test.exec.remote + ;; + + pycodestyle|pep8) + pycodestyle "${basedir}" "${basedir}/bin/cdist" + ;; + + check-pycodestyle) + "$0" pycodestyle + printf "\\nPlease review pycodestyle report.\\n" + while true + do + printf "Continue (yes/no)?\n" + any= + read -r any + case "$any" in + yes) + break + ;; + no) + exit 1 + ;; + *) + printf "Please answer with 'yes' or 'no' explicitly.\n" + ;; + esac + done + ;; + + shellcheck-global-explorers) + # shellcheck disable=SC2086 + find cdist/conf/explorer -type f -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } + ;; + + shellcheck-type-explorers) + # shellcheck disable=SC2086 + find cdist/conf/type -type f -path "*/explorer/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } + ;; + + shellcheck-manifests) + # shellcheck disable=SC2086 + find cdist/conf/type -type f -name manifest -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } + ;; + + shellcheck-local-gencodes) + # shellcheck disable=SC2086 + find cdist/conf/type -type f -name gencode-local -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } + ;; + + shellcheck-remote-gencodes) + # shellcheck disable=SC2086 + find cdist/conf/type -type f -name gencode-remote -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } + ;; + + # NOTE: shellcheck-scripts is kept for compatibility + shellcheck-bin|shellcheck-scripts) + # shellcheck disable=SC2086 + ${SHELLCHECKCMD} bin/cdist-dump bin/cdist-new-type > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } + ;; + + shellcheck-gencodes) + errors=false + "$0" shellcheck-local-gencodes || errors=true + "$0" shellcheck-remote-gencodes || errors=true + ! $errors || exit 1 + ;; + + shellcheck-types) + errors=false + "$0" shellcheck-type-explorers || errors=true + "$0" shellcheck-manifests || errors=true + "$0" shellcheck-gencodes || errors=true + ! $errors || exit 1 + ;; + + shellcheck) + errors=false + "$0" shellcheck-global-explorers || errors=true + "$0" shellcheck-types || errors=true + "$0" shellcheck-bin || errors=true + ! $errors || exit 1 + ;; + + shellcheck-type-files) + # shellcheck disable=SC2086 + find cdist/conf/type -type f -path "*/files/*" -exec ${SHELLCHECKCMD} {} + | grep -v "${SHELLCHECK_SKIP}" > "${SHELLCHECKTMP}" + test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } + ;; + + shellcheck-with-files) + errors=false + "$0" shellcheck || errors=true + "$0" shellcheck-type-files || errors=true + ! $errors || exit 1 + ;; + + shellcheck-build-helper) + ${SHELLCHECKCMD} ./bin/cdist-build-helper + ;; + + check-shellcheck) + "$0" shellcheck + printf "\\nPlease review shellcheck report.\\n" + while true + do + printf "Continue (yes/no)?\n" + any= + read -r any + case "$any" in + yes) + break + ;; + no) + exit 1 + ;; + *) + printf "Please answer with 'yes' or 'no' explicitly.\n" + ;; + esac + done + ;; + + version-branch) + "$0" changelog-version | cut -d. -f '1,2' + ;; + + version) + printf "VERSION = \"%s\"\n" "$(git describe)" > cdist/version.py + ;; + + target-version) + target_version=$($0 changelog-version) + printf "VERSION = \"%s\"\n" "${target_version}" > cdist/version.py + ;; + + clean) + make clean + + # Archlinux + rm -f cdist-*.pkg.tar.xz cdist-*.tar.gz + rm -rf pkg/ src/ + + rm -f MANIFEST PKGBUILD + rm -rf dist/ + + # Signed release + rm -f cdist-*.tar.gz + rm -f cdist-*.tar.gz.asc + + # Temp files + rm -f ./*.tmp + rm -f ./.*.tmp + ;; + + distclean) + "$0" clean + rm -f cdist/version.py + ;; + *) + printf "Unknown target: '%s'.\n" "${option}" >&2 + usage "${basename}" + exit 1 + ;; + +esac diff --git a/bin/cdist-dump b/bin/cdist-dump new file mode 100755 index 00000000..d29e5985 --- /dev/null +++ b/bin/cdist-dump @@ -0,0 +1,326 @@ +#!/bin/sh + +VERSION="0.0.1" +RELEASE="" + +set -u +# set -x + +hosts= +cache_dir=~/.cdist/cache + +do_all=1 +do_global_explorer= +do_type_explorer= +do_script_stdout= +do_script_stderr= +do_gencode= +do_code= +do_messages= +do_parameter= +delimiter=':' +ln= +filename_prefix=1 +verbose=0 + +myname=${0##*/} + +print_version() +{ + printf "%s %s %s\n" "${myname}" "${VERSION}" "${RELEASE}" +} + +usage() +{ + cat << eof +${myname}: [options] [host...] +eof + + print_version + + cat << eof + +Dump data from cache directories. + +host + Dump data for specified hosts. If not specified then all data + from cache directory is dumped. + +Options + -a dump all + -C CACHE-DIR use specified CACHE-DIR (default: ~/.cdist/cache) + -c dump code-* + -d DELIMITER delimiter used for filename and line number prefix (default: ':') + -E dump global explorers + -e dump type explorers + -F disable filename prefix (enabled by default) + -f enable filename prefix (default) + -g dump gencode-* + -h show this help screen and exit + -L disable line number prefix (default) + -l enable line number prefix (disabled by default) + -m dump messages + -o dump executions' stdout + -p dump parameters + -r dump executions' stderr + -V show version and exit + -v increase verbosity +eof +} + +exit_err() +{ + printf "%s\n" "$1" + exit 1 +} + +# parse options +while [ "$#" -ge 1 ] +do + case "$1" in + -a) + do_all=1 + ;; + -C) + if [ "$#" -ge 2 ] + then + case "$2" in + -*) + exit_err "Missing cache directory" + ;; + *) + cache_dir="$2" + shift + ;; + esac + else + exit_err "Missing cache directory" + fi + ;; + -c) + do_code=1 + do_all= + ;; + -d) + if [ "$#" -ge 2 ] + then + case "$2" in + -*) + exit_err "Missing delimiter" + ;; + *) + delimiter="$2" + shift + ;; + esac + else + exit_err "Missing delimiter" + fi + ;; + -E) + do_global_explorer=1 + do_all= + ;; + -e) + do_type_explorer=1 + do_all= + ;; + -F) + filename_prefix= + ;; + -f) + filename_prefix=1 + ;; + -g) + do_gencode=1 + do_all= + ;; + -h) + usage + exit 0 + ;; + -L) + ln= + ;; + -l) + ln=1 + ;; + -m) + do_messages=1 + do_all= + ;; + -o) + do_script_stdout=1 + do_all= + ;; + -p) + do_parameter=1 + do_all= + ;; + -r) + do_script_stderr=1 + do_all= + ;; + -V) + print_version + exit 0 + ;; + -v) + verbose=$((verbose + 1)) + ;; + *) + hosts="${hosts} $1" + break + ;; + esac + shift +done + +if [ "${ln}" = "1" ] +then + ln="NR \"${delimiter}\"" +fi + +if [ "${filename_prefix}" = "1" ] +then + filename_prefix="{}${delimiter}" +fi + +if [ "${do_all}" = "1" ] +then + do_global_explorer=1 + do_type_explorer=1 + do_script_stdout=1 + do_script_stderr=1 + do_gencode=1 + do_code=1 + do_messages=1 + do_parameter=1 +fi + +set -- -size +0 +set -- "$@" \( +or= + +print_verbose() +{ + if [ "${verbose}" -ge "$1" ] + then + printf "%s\n" "$2" + fi +} + +hor_line() +{ + if [ $# -gt 0 ] + then + c="$1" + else + c='=' + fi + printf "%78s\n" "" | tr ' ' "${c}" +} + +if [ "${do_global_explorer}" ] +then + print_verbose 2 "Dumping global explorers" + # shellcheck disable=SC2086 + set -- "$@" ${or} \( \ + -path "*/explorer/*" -a \ + ! -path "*/conf/*" -a \ + ! -path "*/object/*/explorer/*" \ + \) + or="-o" +fi + +if [ "${do_type_explorer}" ] +then + print_verbose 2 "Dumping type explorers" + set -- "$@" ${or} -path "*/object/*/explorer/*" + or="-o" +fi + +if [ "${do_script_stdout}" ] +then + print_verbose 2 "Dumping execution's stdout" + set -- "$@" ${or} -path "*/stdout/*" + or="-o" +fi + +if [ "${do_script_stderr}" ] +then + print_verbose 2 "Dumping execution's stderr" + set -- "$@" ${or} -path "*/stderr/*" + or="-o" +fi + +if [ "${do_gencode}" ] +then + print_verbose 2 "Dumping gencode-*" + set -- "$@" ${or} \( -name "gencode-*" -a ! -path "*/stdout/*" -a ! -path "*/stderr/*" \) + or="-o" +fi + +if [ "${do_code}" ] +then + print_verbose 2 "Dumping code-*" + set -- "$@" ${or} \( -name "code-*" -a ! -path "*/stdout/*" -a ! -path "*/stderr/*" \) + or="-o" +fi + +if [ "${do_messages}" ] +then + print_verbose 2 "Dumping messages" + set -- "$@" ${or} -name "messages" + or="-o" +fi + +if [ "${do_parameter}" ] +then + print_verbose 2 "Dumping parameters" + set -- "$@" ${or} -path "*/parameter/*" + or="-o" +fi + +set -- "$@" \) +set -- '.' "$@" -exec awk -v prefix="${filename_prefix}" "{print prefix ${ln} \$0}" {} \; + +# printf "+ %s\n" "$*" + +print_verbose 2 "Using cache dir: ${cache_dir}" + +OLD_PWD=$(pwd) +cd "${cache_dir}" || exit + +# If no host is specified then search all. +[ -z "${hosts}" ] && hosts="-" + +for host in ${hosts} +do + [ "${host}" = "-" ] && host= + # find host cache directory + host_dir=$(find . -name target_host -exec grep -l "${host}" {} +) + print_verbose 3 "found host directory files:" + print_verbose 3 "${host_dir}" + + OLD_IFS="${IFS}" + IFS=" + " + + for d in ${host_dir} + do + dir=$(dirname "${d}") + + print_verbose 0 "target host: $(cat "${dir}/target_host"), host directory: ${dir}" + hor_line '=' + + PREV_PWD=$(pwd) + cd "${dir}" || exit + # set -x + find "$@" + # set +x + cd "${PREV_PWD}" || exit + done + IFS="${OLD_IFS}" +done +cd "${OLD_PWD}" || exit diff --git a/bin/cdist-new-type b/bin/cdist-new-type new file mode 100755 index 00000000..79dcfd90 --- /dev/null +++ b/bin/cdist-new-type @@ -0,0 +1,159 @@ +#!/bin/sh + +basename="${0##*/}" + +if [ $# -lt 3 ] +then + printf "usage: %s TYPE-NAME AUTHOR-NAME AUTHOR-EMAIL [TYPE-BASE-PATH] + TYPE-NAME Name of the type. + AUTHOR-NAME Type author's full name. + AUTHOR-EMAIL Type author's email. + TYPE-BASE-PATH Path to the base directory of the type. If not set it defaults + to '\$PWD/type'.\n" "${basename}" + exit 1 +fi + +type_name="$1" +shift +author_name="$1" +shift +author_email="$1" +shift + +if [ $# -ge 1 ] +then + type_base_path="$1" + shift +else + #type_base_path=~/.cdist/type + type_base_path="$PWD/type" +fi + +error() { + printf "%s\n" "$*" >&2 +} + +die() { + error "$@" + exit 1 +} + +cd "$type_base_path" || die "Could not change to type directory: $type_base_path. +You have to specify type base path or run me from within a cdist conf directory, +e.g. ~/.cdist." + +year=$(date +%Y) +copyright="# $year $author_name ($author_email)" + +license="# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +" + +set -e + +mkdir "$type_name" +cd "$type_name" + +### man page +header="cdist-type${type_name}(7)" +header_length="${#header}" +cat >> man.rst << DONE +$header +$(while [ "${header_length}" -gt 0 ]; do printf "="; header_length=$((header_length - 1)); done; printf "\n";) + +NAME +---- +cdist-type${type_name} - TODO + + +DESCRIPTION +----------- +This space intentionally left blank. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +None. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # TODO + ${type_name} + + +SEE ALSO +-------- +:strong:\`TODO\`\\ (7) + + +AUTHORS +------- +$author_name <$author_email> + + +COPYING +------- +Copyright \(C) $year $author_name. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. +DONE + +### manifest +cat >> manifest << DONE +#!/bin/sh -e +# +${copyright} +# +${license} + +os=\$(cat "\$__global/explorer/os") + +case "\$os" in + *) + printf "Your operating system (%s) is currently not supported by this type (%s)\n" "\$os" "\${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + ;; +esac +DONE +chmod +x manifest + +# gencode-remote +cat >> gencode-remote << DONE +#!/bin/sh -e +# +${copyright} +# +${license} +DONE +chmod +x gencode-remote + +printf "%s/%s\n" "$type_base_path" "$type_name" diff --git a/cdist/__init__.py b/cdist/__init__.py index e6fdfac6..44366cd0 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -22,11 +22,27 @@ import os import hashlib +import subprocess import cdist.log -import cdist.version -VERSION = cdist.version.VERSION + +VERSION = 'unknown version' + +try: + import cdist.version + VERSION = cdist.version.VERSION +except ModuleNotFoundError: + cdist_dir = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir)) + if os.path.isdir(os.path.join(cdist_dir, '.git')): + try: + VERSION = subprocess.check_output( + ['git', 'describe', '--always'], + cwd=cdist_dir, + universal_newlines=True) + except Exception: + pass BANNER = """ .. . .x+=:. s @@ -48,6 +64,9 @@ REMOTE_EXEC = "ssh -o User=root" REMOTE_CMDS_CLEANUP_PATTERN = "ssh -o User=root -O exit -S {}" +MIN_SUPPORTED_PYTHON_VERSION = '3.5' + + class Error(Exception): """Base exception class for this project""" pass @@ -83,41 +102,78 @@ class CdistBetaRequired(cdist.Error): class CdistEntityError(Error): """Something went wrong while executing cdist entity""" - def __init__(self, entity_name, entity_params, stderr_paths, subject=''): + def __init__(self, entity_name, entity_params, stdout_paths, + stderr_paths, subject=''): self.entity_name = entity_name self.entity_params = entity_params self.stderr_paths = stderr_paths + self.stdout_paths = stdout_paths if isinstance(subject, Error): self.original_error = subject else: self.original_error = None self.message = str(subject) + def _stdpath(self, stdpaths, header_name): + result = {} + for name, path in stdpaths: + if name not in result: + result[name] = [] + try: + if os.path.exists(path) and os.path.getsize(path) > 0: + output = [] + label_begin = name + ":" + header_name + output.append(label_begin) + output.append('\n') + output.append('-' * len(label_begin)) + output.append('\n') + with open(path, 'r') as fd: + output.append(fd.read()) + output.append('\n') + result[name].append(''.join(output)) + except UnicodeError as ue: + result[name].append(('Cannot output {}:{} due to: {}.\n' + 'You can try to read the error file "{}"' + ' yourself.').format( + name, header_name, ue, path)) + return result + + def _stderr(self): + return self._stdpath(self.stderr_paths, 'stderr') + + def _stdout(self): + return self._stdpath(self.stdout_paths, 'stdout') + + def _update_dict_list(self, target, source): + for x in source: + if x not in target: + target[x] = [] + target[x].extend(source[x]) + @property - def stderr(self): - output = [] - for stderr_name, stderr_path in self.stderr_paths: - if (os.path.exists(stderr_path) and - os.path.getsize(stderr_path) > 0): - label_begin = '---- BEGIN ' + stderr_name + ':stderr ----' - label_end = '---- END ' + stderr_name + ':stderr ----' - output.append('\n' + label_begin) - with open(stderr_path, 'r') as fd: - output.append(fd.read()) - output.append(label_end) - return '\n'.join(output) + def std_streams(self): + std_dict = {} + self._update_dict_list(std_dict, self._stdout()) + self._update_dict_list(std_dict, self._stderr()) + return std_dict def __str__(self): output = [] output.append(self.message) - header = "\nError processing " + self.entity_name + output.append('\n\n') + header = "Error processing " + self.entity_name under_header = '=' * len(header) output.append(header) + output.append('\n') output.append(under_header) + output.append('\n') for param_name, param_value in self.entity_params: output.append(param_name + ': ' + str(param_value)) - output.append(self.stderr + '\n') - return '\n'.join(output) + output.append('\n') + output.append('\n') + for x in self.std_streams: + output.append(''.join(self.std_streams[x])) + return ''.join(output) class CdistObjectError(CdistEntityError): @@ -127,28 +183,76 @@ class CdistObjectError(CdistEntityError): ('name', cdist_object.name, ), ('path', cdist_object.absolute_path, ), ('source', " ".join(cdist_object.source), ), - ('type', cdist_object.cdist_type.absolute_path, ), + ('type', os.path.realpath( + cdist_object.cdist_type.absolute_path), ), ] stderr_paths = [] for stderr_name in os.listdir(cdist_object.stderr_path): stderr_path = os.path.join(cdist_object.stderr_path, stderr_name) stderr_paths.append((stderr_name, stderr_path, )) + stdout_paths = [] + for stdout_name in os.listdir(cdist_object.stdout_path): + stdout_path = os.path.join(cdist_object.stdout_path, + stdout_name) + stdout_paths.append((stdout_name, stdout_path, )) super().__init__("object '{}'".format(cdist_object.name), - params, stderr_paths, subject) + params, stdout_paths, stderr_paths, subject) + + +class CdistObjectExplorerError(CdistEntityError): + """ + Something went wrong while working on a specific + cdist object explorer + """ + def __init__(self, cdist_object, explorer_name, explorer_path, + stderr_path, subject=''): + params = [ + ('object name', cdist_object.name, ), + ('object path', cdist_object.absolute_path, ), + ('object source', " ".join(cdist_object.source), ), + ('object type', os.path.realpath( + cdist_object.cdist_type.absolute_path), ), + ('explorer name', explorer_name, ), + ('explorer path', explorer_path, ), + ] + stdout_paths = [] + stderr_paths = [ + ('remote', stderr_path, ), + ] + super().__init__("explorer '{}' of object '{}'".format( + explorer_name, cdist_object.name), params, stdout_paths, + stderr_paths, subject) class InitialManifestError(CdistEntityError): """Something went wrong while executing initial manifest""" - def __init__(self, initial_manifest, stderr_path, subject=''): + def __init__(self, initial_manifest, stdout_path, stderr_path, subject=''): params = [ ('path', initial_manifest, ), ] - stderr_paths = [] + stdout_paths = [ + ('init', stdout_path, ), + ] stderr_paths = [ ('init', stderr_path, ), ] - super().__init__('initial manifest', params, stderr_paths, subject) + super().__init__('initial manifest', params, stdout_paths, + stderr_paths, subject) + + +class GlobalExplorerError(CdistEntityError): + """Something went wrong while executing global explorer""" + def __init__(self, name, path, stderr_path, subject=''): + params = [ + ('name', name, ), + ('path', path, ), + ] + stderr_paths = [ + ('remote', stderr_path, ), + ] + super().__init__("global explorer '{}'".format(name), + params, [], stderr_paths, subject) def file_to_list(filename): diff --git a/cdist/argparse.py b/cdist/argparse.py index 29620751..88759d7b 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -5,21 +5,26 @@ import logging import collections import functools import cdist.configuration +import cdist.log +import cdist.preos +import cdist.info +import cdist.scan.commandline # set of beta sub-commands -BETA_COMMANDS = set(('install', 'inventory', )) +BETA_COMMANDS = set(('install', 'inventory', 'scan', )) # set of beta arguments for sub-commands BETA_ARGS = { - 'config': set(('jobs', 'tag', 'all_tagged_hosts', 'use_archiving', )), + 'config': set(('tag', 'all_tagged_hosts', 'use_archiving', )), } -EPILOG = "Get cdist at http://www.nico.schottelius.org/software/cdist/" +EPILOG = "Get cdist at https://code.ungleich.ch/ungleich-public/cdist" # Parser others can reuse parser = None _verbosity_level_off = -2 _verbosity_level = { + None: logging.WARNING, _verbosity_level_off: logging.OFF, -1: logging.ERROR, 0: logging.WARNING, @@ -101,7 +106,7 @@ def get_parsers(): name="log level"), help=('Set the specified verbosity level. ' 'The levels, in order from the lowest to the highest, are: ' - 'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) ' + 'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3), ' 'TRACE (4 or higher). If used along with -v then -v ' 'increases last set value and -l overwrites last set ' 'value.'), @@ -122,6 +127,14 @@ def get_parsers(): 'value.'), action='count', default=None) + parser['colored_output'] = argparse.ArgumentParser(add_help=False) + parser['colored_output'].add_argument( + '--colors', metavar='WHEN', + help="Colorize cdist's output based on log level; " + "WHEN is 'always', 'never', or 'auto'.", + action='store', dest='colored_output', required=False, + choices=cdist.configuration.ColoredOutputOption.CHOICES) + parser['beta'] = argparse.ArgumentParser(add_help=False) parser['beta'].add_argument( '-b', '--beta', @@ -161,6 +174,16 @@ def get_parsers(): # Config parser['config_main'] = argparse.ArgumentParser(add_help=False) + parser['config_main'].add_argument( + '-4', '--force-ipv4', + help=('Force to use IPv4 addresses only. No influence for custom' + ' remote commands.'), + action='store_const', dest='force_ipv', const=4) + parser['config_main'].add_argument( + '-6', '--force-ipv6', + help=('Force to use IPv6 addresses only. No influence for custom' + ' remote commands.'), + action='store_const', dest='force_ipv', const=6) parser['config_main'].add_argument( '-C', '--cache-path-pattern', help=('Specify custom cache path pattern. If ' @@ -181,16 +204,27 @@ def get_parsers(): name="positive int"), help=('Operate in parallel in specified maximum number of jobs. ' 'Global explorers, object prepare and object run are ' - 'supported. Without argument CPU count is used by default. ' - 'Currently in beta.'), + 'supported. Without argument CPU count is used by default. '), action='store', dest='jobs', const=multiprocessing.cpu_count()) + parser['config_main'].add_argument( + '--log-server', + action='store_true', + help=('Start a log server for sub processes to use. ' + 'This is mainly useful when running cdist nested ' + 'from a code-local script. Log server is alwasy ' + 'implicitly started for \'install\' command.')) parser['config_main'].add_argument( '-n', '--dry-run', help='Do not execute code.', action='store_true') parser['config_main'].add_argument( '-o', '--out-dir', help='Directory to save cdist output in.', dest="out_path") + parser['config_main'].add_argument( + '-P', '--timestamp', + help=('Timestamp log messages with the current local date and time ' + 'in the format: YYYYMMDDHHMMSS.us.'), + action='store_true', dest='timestamp') parser['config_main'].add_argument( '-R', '--use-archiving', nargs='?', choices=('tar', 'tgz', 'tbz2', 'txz',), @@ -220,19 +254,13 @@ def get_parsers(): '(should behave like ssh).'), action='store', dest='remote_exec', default=None) + parser['config_main'].add_argument( + '-S', '--disable-saving-output-streams', + help='Disable saving output streams.', + action='store_false', dest='save_output_streams', default=True) # Config parser['config_args'] = argparse.ArgumentParser(add_help=False) - parser['config_args'].add_argument( - '-4', '--force-ipv4', - help=('Force to use IPv4 addresses only. No influence for custom' - ' remote commands.'), - action='store_const', dest='force_ipv', const=4) - parser['config_args'].add_argument( - '-6', '--force-ipv6', - help=('Force to use IPv6 addresses only. No influence for custom' - ' remote commands.'), - action='store_const', dest='force_ipv', const=6) parser['config_args'].add_argument( '-A', '--all-tagged', help=('Use all hosts present in tags db. Currently in beta.'), @@ -242,14 +270,11 @@ def get_parsers(): help=('List hosts that have all specified tags, ' 'if -t/--tag is specified.'), action="store_true", dest="has_all_tags", default=False) - parser['config_args'].add_argument( - 'host', nargs='*', help='Host(s) to operate on.') parser['config_args'].add_argument( '-f', '--file', help=('Read specified file for a list of additional hosts to ' 'operate on or if \'-\' is given, read stdin (one host per ' - 'line). If no host or host file is specified then, by ' - 'default, read hosts from stdin.'), + 'line).'), dest='hostfile', required=False) parser['config_args'].add_argument( '-p', '--parallel', nargs='?', metavar='HOST_MAX', @@ -260,10 +285,6 @@ def get_parsers(): 'default.'), action='store', dest='parallel', const=multiprocessing.cpu_count()) - parser['config_args'].add_argument( - '-S', '--disable-saving-output-streams', - help='Disable saving output streams.', - action='store_false', dest='save_output_streams', default=True) parser['config_args'].add_argument( '-s', '--sequential', help='Operate on multiple hosts sequentially (default).', @@ -274,8 +295,11 @@ def get_parsers(): 'list all hosts that contain any of specified tags. ' 'Currently in beta.'), dest='tag', required=False, action="store_true", default=False) + parser['config_args'].add_argument( + 'host', nargs='*', help='Host(s) to operate on.') parser['config'] = parser['sub'].add_parser( 'config', parents=[parser['loglevel'], parser['beta'], + parser['colored_output'], parser['common'], parser['config_main'], parser['inventory_common'], @@ -294,6 +318,7 @@ def get_parsers(): parser['add-host'] = parser['invsub'].add_parser( 'add-host', parents=[parser['loglevel'], parser['beta'], + parser['colored_output'], parser['common'], parser['inventory_common']]) parser['add-host'].add_argument( @@ -301,13 +326,12 @@ def get_parsers(): parser['add-host'].add_argument( '-f', '--file', help=('Read additional hosts to add from specified file ' - 'or from stdin if \'-\' (each host on separate line). ' - 'If no host or host file is specified then, by default, ' - 'read from stdin.'), + 'or from stdin if \'-\' (each host on separate line). '), dest='hostfile', required=False) parser['add-tag'] = parser['invsub'].add_parser( 'add-tag', parents=[parser['loglevel'], parser['beta'], + parser['colored_output'], parser['common'], parser['inventory_common']]) parser['add-tag'].add_argument( @@ -316,20 +340,12 @@ def get_parsers(): parser['add-tag'].add_argument( '-f', '--file', help=('Read additional hosts to add tags from specified file ' - 'or from stdin if \'-\' (each host on separate line). ' - 'If no host or host file is specified then, by default, ' - 'read from stdin. If no tags/tagfile nor hosts/hostfile' - ' are specified then tags are read from stdin and are' - ' added to all hosts.'), + 'or from stdin if \'-\' (each host on separate line). '), dest='hostfile', required=False) parser['add-tag'].add_argument( '-T', '--tag-file', help=('Read additional tags to add from specified file ' - 'or from stdin if \'-\' (each tag on separate line). ' - 'If no tag or tag file is specified then, by default, ' - 'read from stdin. If no tags/tagfile nor hosts/hostfile' - ' are specified then tags are read from stdin and are' - ' added to all hosts.'), + 'or from stdin if \'-\' (each tag on separate line). '), dest='tagfile', required=False) parser['add-tag'].add_argument( '-t', '--taglist', @@ -339,6 +355,7 @@ def get_parsers(): parser['del-host'] = parser['invsub'].add_parser( 'del-host', parents=[parser['loglevel'], parser['beta'], + parser['colored_output'], parser['common'], parser['inventory_common']]) parser['del-host'].add_argument( @@ -349,13 +366,12 @@ def get_parsers(): parser['del-host'].add_argument( '-f', '--file', help=('Read additional hosts to delete from specified file ' - 'or from stdin if \'-\' (each host on separate line). ' - 'If no host or host file is specified then, by default, ' - 'read from stdin.'), + 'or from stdin if \'-\' (each host on separate line). '), dest='hostfile', required=False) parser['del-tag'] = parser['invsub'].add_parser( 'del-tag', parents=[parser['loglevel'], parser['beta'], + parser['colored_output'], parser['common'], parser['inventory_common']]) parser['del-tag'].add_argument( @@ -368,20 +384,13 @@ def get_parsers(): parser['del-tag'].add_argument( '-f', '--file', help=('Read additional hosts to delete tags for from specified ' - 'file or from stdin if \'-\' (each host on separate line). ' - 'If no host or host file is specified then, by default, ' - 'read from stdin. If no tags/tagfile nor hosts/hostfile' - ' are specified then tags are read from stdin and are' - ' deleted from all hosts.'), + 'file or from stdin if \'-\' (each host on separate ' + 'line). '), dest='hostfile', required=False) parser['del-tag'].add_argument( '-T', '--tag-file', help=('Read additional tags from specified file ' - 'or from stdin if \'-\' (each tag on separate line). ' - 'If no tag or tag file is specified then, by default, ' - 'read from stdin. If no tags/tagfile nor' - ' hosts/hostfile are specified then tags are read from' - ' stdin and are added to all hosts.'), + 'or from stdin if \'-\' (each tag on separate line). '), dest='tagfile', required=False) parser['del-tag'].add_argument( '-t', '--taglist', @@ -391,6 +400,7 @@ def get_parsers(): parser['list'] = parser['invsub'].add_parser( 'list', parents=[parser['loglevel'], parser['beta'], + parser['colored_output'], parser['common'], parser['inventory_common']]) parser['list'].add_argument( @@ -418,15 +428,78 @@ def get_parsers(): parser['inventory'].set_defaults( func=cdist.inventory.Inventory.commandline) + # PreOS + parser['preos'] = parser['sub'].add_parser('preos', add_help=False) + # Shell parser['shell'] = parser['sub'].add_parser( - 'shell', parents=[parser['loglevel']]) + 'shell', parents=[parser['loglevel'], parser['colored_output']]) parser['shell'].add_argument( '-s', '--shell', help=('Select shell to use, defaults to current shell. Used shell' ' should be POSIX compatible shell.')) parser['shell'].set_defaults(func=cdist.shell.Shell.commandline) + # Info + parser['info'] = parser['sub'].add_parser('info') + parser['info'].add_argument( + '-a', '--all', help='Display all info. This is the default.', + action='store_true', default=False) + parser['info'].add_argument( + '-c', '--conf-dir', + help='Add configuration directory (can be repeated).', + action='append') + parser['info'].add_argument( + '-e', '--global-explorers', + help='Display info for global explorers.', action='store_true', + default=False) + parser['info'].add_argument( + '-F', '--fixed-string', + help='Interpret pattern as a fixed string.', action='store_true', + default=False) + parser['info'].add_argument( + '-f', '--full', help='Display full details.', + action='store_true', default=False) + parser['info'].add_argument( + '-g', '--config-file', + help='Use specified custom configuration file.', + dest="config_file", required=False) + parser['info'].add_argument( + '-t', '--types', help='Display info for types.', + action='store_true', default=False) + parser['info'].add_argument( + 'pattern', nargs='?', help='Glob pattern.') + parser['info'].set_defaults(func=cdist.info.Info.commandline) + + # Scan = config + further + parser['scan'] = parser['sub'].add_parser('scan', add_help=False, + parents=[parser['config']]) + + parser['scan'] = parser['sub'].add_parser( + 'scan', parents=[parser['loglevel'], + parser['beta'], + parser['colored_output'], + parser['common'], + parser['config_main']]) + + parser['scan'].add_argument( + '-m', '--mode', help='Which modes should run', + action='append', default=[], + choices=['scan', 'trigger']) + parser['scan'].add_argument( + '--config', + action='store_true', + help='Try to configure detected hosts') + parser['scan'].add_argument( + '-I', '--interfaces', + action='append', default=[], + help='On which interfaces to scan/trigger') + parser['scan'].add_argument( + '-d', '--delay', + action='store', default=3600, + help='How long to wait before reconfiguring after last try') + parser['scan'].set_defaults(func=cdist.scan.commandline.commandline) + for p in parser: parser[p].epilog = EPILOG @@ -437,7 +510,12 @@ def handle_loglevel(args): if hasattr(args, 'quiet') and args.quiet: args.verbose = _verbosity_level_off - logging.root.setLevel(_verbosity_level[args.verbose]) + logging.getLogger().setLevel(_verbosity_level[args.verbose]) + + +def handle_log_colors(args): + if cdist.configuration.ColoredOutputOption.translate(args.colored_output): + cdist.log.CdistFormatter.USE_COLORS = True def parse_and_configure(argv, singleton=True): @@ -451,6 +529,7 @@ def parse_and_configure(argv, singleton=True): raise cdist.Error(str(e)) # Loglevels are handled globally in here handle_loglevel(args) + handle_log_colors(args) log = logging.getLogger("cdist") diff --git a/cdist/conf/explorer/cpu_cores b/cdist/conf/explorer/cpu_cores index 7f7a955e..81e5294e 100755 --- a/cdist/conf/explorer/cpu_cores +++ b/cdist/conf/explorer/cpu_cores @@ -25,13 +25,22 @@ os=$("$__explorer/os") case "$os" in "macosx") - echo "$(sysctl -n hw.physicalcpu)" + sysctl -n hw.physicalcpu + ;; + + "openbsd") + sysctl -n hw.ncpuonline + ;; + + "freebsd"|"netbsd") + PATH=$(getconf PATH) + sysctl -n hw.ncpu ;; *) if [ -r /proc/cpuinfo ]; then cores="$(grep "core id" /proc/cpuinfo | sort | uniq | wc -l)" - if [ ${cores} -eq 0 ]; then + if [ "${cores}" -eq 0 ]; then cores="1" fi echo "$cores" diff --git a/cdist/conf/explorer/cpu_sockets b/cdist/conf/explorer/cpu_sockets index 8a8194df..a32e2f00 100755 --- a/cdist/conf/explorer/cpu_sockets +++ b/cdist/conf/explorer/cpu_sockets @@ -25,14 +25,14 @@ os=$("$__explorer/os") case "$os" in "macosx") - echo "$(system_profiler SPHardwareDataType | grep "Number of Processors" | awk -F': ' '{print $2}')" + system_profiler SPHardwareDataType | grep "Number of Processors" | awk -F': ' '{print $2}' ;; *) if [ -r /proc/cpuinfo ]; then - sockets="$(grep "physical id" /proc/cpuinfo | sort | uniq | wc -l)" - if [ ${sockets} -eq 0 ]; then - sockets="$(cat /proc/cpuinfo | grep "processor" | wc -l)" + sockets="$(grep "physical id" /proc/cpuinfo | sort -u | wc -l)" + if [ "${sockets}" -eq 0 ]; then + sockets="$(grep -c "processor" /proc/cpuinfo)" fi echo "${sockets}" fi diff --git a/cdist/conf/explorer/disks b/cdist/conf/explorer/disks old mode 100644 new mode 100755 index 52fef81e..56d62d10 --- a/cdist/conf/explorer/disks +++ b/cdist/conf/explorer/disks @@ -1,2 +1,66 @@ -cd /dev -echo sd? hd? vd? +#!/bin/sh -e +# +# based on previous work by other people, modified by: +# 2020 Dennis Camera +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Finds disks of the system (excl. ram disks, floppy, cdrom) + +uname_s="$(uname -s)" + +case $uname_s in + FreeBSD) + sysctl -n kern.disks + ;; + OpenBSD) + sysctl -n hw.disknames | grep -Eo '[lsw]d[0-9]+' + ;; + NetBSD) + PATH=$(getconf PATH) + sysctl -n hw.disknames | awk -v RS=' ' '/^[lsw]d[0-9]+/' + ;; + Linux) + # list of major device numbers toexclude: + # ram disks, floppies, cdroms + # https://www.kernel.org/doc/Documentation/admin-guide/devices.txt + ign_majors='1 2 11' + + if command -v lsblk >/dev/null 2>&1 + then + lsblk -e "$(echo "$ign_majors" | tr ' ' ',')" -dno name + elif test -d /sys/block/ + then + # shellcheck disable=SC2012 + ls -1 /sys/block/ \ + | awk -v ign_majors="$(echo "$ign_majors" | tr ' ' '|')" ' + { + devfile = "/sys/block/" $0 "/dev" + getline devno < devfile + close(devfile) + if (devno !~ "^(" ign_majors "):") print + }' + else + echo "Don't know how to list disks on Linux without lsblk and sysfs." >&2 + echo 'If you can, please submit a patch.'>&2 + fi + ;; + *) + printf "Don't know how to list disks for %s operating system.\n" "${uname_s}" >&2 + printf 'If you can please submit a patch\n' >&2 + ;; +esac \ +| xargs diff --git a/cdist/conf/explorer/hostname b/cdist/conf/explorer/hostname index 7715c6b0..dca004d1 100755 --- a/cdist/conf/explorer/hostname +++ b/cdist/conf/explorer/hostname @@ -1,7 +1,6 @@ #!/bin/sh # -# 2010-2014 Nico Schottelius (nico-cdist at schottelius.org) -# 2012 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,7 +18,12 @@ # along with cdist. If not, see . # # +# Retrieve the running hostname +# -if command -v uname >/dev/null; then - uname -n +if command -v hostname >/dev/null +then + hostname +else + uname -n fi diff --git a/cdist/conf/explorer/init b/cdist/conf/explorer/init index 43a66a50..f27c77ef 100755 --- a/cdist/conf/explorer/init +++ b/cdist/conf/explorer/init @@ -1,7 +1,8 @@ -#!/bin/sh +#!/bin/sh -e # # 2016 Daniel Heule (hda at sfs.biz) # Copyright 2017, Philippe Gregoire +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,21 +20,423 @@ # along with cdist. If not, see . # # -# Returns the process name of pid 1 ( normaly the init system ) -# for example at linux this value is "init" or "systemd" in most cases +# Returns the name of the init system (PID 1) + +# Expected values: +# Linux: +# Adélie Linux: +# sysvinit+openrc +# Alpine Linux: +# busybox-init+openrc +# ArchLinux: +# systemd, sysvinit +# CRUX: +# sysvinit +# Debian: +# systemd, upstart, sysvinit, openrc, ??? +# Devuan: +# sysvinit, sysvinit+openrc +# Gentoo: +# sysvinit+openrc, openrc-init, systemd +# OpenBMC: +# systemd +# OpenWrt: +# procd, init??? +# RedHat (RHEL, CentOS, Fedora, RedHat Linux, ...): +# systemd, upstart, upstart-legacy, sysvinit +# Slackware: +# sysvinit +# SuSE: +# systemd, sysvinit +# Ubuntu: +# systemd, upstart, upstart-legacy, sysvinit +# VoidLinux: +# runit # +# GNU: +# Debian: +# sysvinit, hurd-init +# +# BSD: +# {Free,Open,Net}BSD: +# init +# +# Mac OS X: +# launchd, init+SystemStarter +# +# Solaris/Illumos: +# smf, init??? -uname_s="$(uname -s)" +# NOTE: init systems can be stacked. This is popular to run OpenRC on top of +# sysvinit (Gentoo) or busybox-init (Alpine), but can also be used to run runit +# as a systemd service. This makes init system detection very complicated +# (which result is expected?) This script tries to untangle some combinations, +# OpenRC on top of sysv or busybox (X+openrc), but will ignore others (runit as +# a systemd service) -case "$uname_s" in - Linux) - (pgrep -P0 -l | awk '/^1[ \t]/ {print $2;}') || true - ;; - FreeBSD) - ps -o comm= -p 1 || true - ;; - *) - # return a empty string as unknown value - echo "" - ;; -esac +# NOTE: When we have no idea, nothing will be printed! + +# NOTE: +# When trying to gather information about the init system make sure to do so +# without calling the binary! On some systems this triggers a reinitialisation +# of the system which we don't want (e.g. embedded systems). + + +set -e + +KERNEL_NAME=$(uname -s) + +KNOWN_INIT_SYSTEMS=$(cat </dev/null 2>&1 || return 1 + launchctl getenv PATH >/dev/null || return 1 + echo launchd +} + +check_openrc() { + test -f /run/openrc/softlevel || return 1 + echo openrc +} + +check_procd() ( + procd_path=${1:-/sbin/procd} + test -x "${procd_path}" || return 1 + grep -q 'procd' "${procd_path}" || return 1 + echo procd +) + +check_runit() { + test -d /run/runit || return 1 + echo runit +} + +check_smf() { + # XXX: Is this the correct way?? + test -f /etc/svc/volatile/svc_nonpersist.db || return 1 + echo smf +} + +check_systemd() { + # NOTE: sd_booted(3) + test -d /run/systemd/system/ || return 1 + # systemctl --version | sed -e '/^systemd/!d;s/^systemd //' + echo systemd +} + +check_systemstarter() { + test -d /System/Library/StartupItems/ || return 1 + test -f /System/Library/StartupItems/LoginWindow/StartupParameters.plist || return 1 + echo init+SystemStarter +} + +check_sysvinit() ( + init_path=${1:-/sbin/init} + test -x "${init_path}" || return 1 + grep -q 'INIT_VERSION=sysvinit-[0-9.]*' "${init_path}" || return 1 + + # It is quite common to use SysVinit to stack other init systemd + # (like OpenRC) on top of it. So we check for that, too. + if stacked=$(check_openrc) + then + echo "sysvinit+${stacked}" + else + echo sysvinit + fi + unset stacked +) + +check_upstart() { + test -x "$(command -v initctl)" || return 1 + case $(initctl version) + in + *'(upstart '*')') + if test -d /etc/init + then + # modern (DBus-based?) upstart >= 0.5 + echo upstart + elif test -d /etc/event.d + then + # ancient upstart + echo upstart-legacy + else + # whatever... + echo upstart + fi + ;; + *) + return 1 + ;; + esac +} + +find_init_procfs() ( + # First, check if the required file in procfs exists... + test -h /proc/1/exe || return 1 + + # Find init executable + init_exe=$(ls -l /proc/1/exe 2>/dev/null) || return 1 + init_exe=${init_exe#* -> } + + if ! test -x "$init_exe" + then + # On some rare occasions it can happen that the + # running init's binary has been replaced. In this + # case Linux adjusts the symlink to "X (deleted)" + + # [root@fedora-12 ~]# readlink /proc/1/exe + # /sbin/init (deleted) + # [root@fedora-12 ~]# ls -l /proc/1/exe + # lrwxrwxrwx. 1 root root 0 2020-01-30 23:00 /proc/1/exe -> /sbin/init (deleted) + + init_exe=${init_exe% (deleted)} + test -x "$init_exe" || return 1 + fi + + echo "${init_exe}" +) + +guess_by_path() { + case $1 + in + /bin/busybox) + check_busybox_init "$1" && return + ;; + /lib/systemd/systemd) + check_systemd "$1" && return + ;; + /hurd/init) + check_hurd_init "$1" && return + ;; + /sbin/launchd) + check_launchd "$1" && return + ;; + /usr/bin/runit|/sbin/runit) + check_runit "$1" && return + ;; + /sbin/openrc-init) + if check_openrc "$1" >/dev/null + then + echo openrc-init + return + fi + ;; + /sbin/procd) + check_procd "$1" && return + ;; + /sbin/init|*/init) + # init: it could be anything -> (explicit) no match + return 1 + ;; + esac + + # No match + return 1 +} + +guess_by_comm_name() { + case $1 + in + busybox) + check_busybox_init && return + ;; + openrc-init) + if check_openrc >/dev/null + then + echo openrc-init + return 0 + fi + ;; + init) + # init could be anything -> no match + return 1 + ;; + *) + # Run check function by comm name if available. + # Fall back to comm name if either it does not exist or + # returns non-zero. + if type "check_$1" >/dev/null + then + "check_$1" && return + else + echo "$1" ; return 0 + fi + esac + + return 1 +} + +check_list() ( + # List must be a multi-line input on stdin (one name per line) + while read -r init + do + "check_${init}" || continue + return 0 + done + return 1 +) + + +# BusyBox's versions of ps and pgrep do not support some options +# depending on which compile-time options have been used. + +find_init_pgrep() { + pgrep -P0 -fl 2>/dev/null | awk -F '[[:blank:]]' '$1 == 1 { print $2 }' +} + +find_init_ps() { + case $KERNEL_NAME + in + Darwin) + ps -o command -p 1 2>/dev/null | tail -n +2 + ;; + FreeBSD) + ps -o args= -p 1 2>/dev/null | cut -d ' ' -f 1 + ;; + Linux) + ps -o comm= -p 1 2>/dev/null + ;; + NetBSD) + ps -o comm= -p 1 2>/dev/null + ;; + OpenBSD) + ps -o args -p 1 2>/dev/null | tail -n +2 | cut -d ' ' -f 1 + ;; + *) + ps -o args= -p 1 2>/dev/null + ;; + esac | trim # trim trailing whitespace (some ps like Darwin add it) +} + +find_init() { + case $KERNEL_NAME + in + Linux|GNU|NetBSD) + find_init_procfs || find_init_pgrep || find_init_ps + ;; + FreeBSD) + find_init_procfs || find_init_ps + ;; + OpenBSD) + find_init_pgrep || find_init_ps + ;; + Darwin|SunOS) + find_init_ps + ;; + *) + echo "Don't know how to determine init." >&2 + echo 'Please send a patch.' >&2 + exit 1 + esac +} + +# ----- + +init=$(find_init) + +# If we got a path, guess by the path first (fall back to file name if no match) +# else guess by file name directly. +# shellcheck disable=SC2015 +{ + test -x "${init}" \ + && guess_by_path "${init}" \ + || guess_by_comm_name "$(basename "${init}")" +} && exit 0 || true + + +# Guessing based on the file path and name didn’t lead to a definitive result. +# +# We go through all of the checks until we find a match. To speed up the +# process, common cases will be checked first based on the underlying kernel. + +{ common_candidates_by_kernel; echo "${KNOWN_INIT_SYSTEMS}"; } \ + | unique | check_list diff --git a/cdist/conf/explorer/interfaces b/cdist/conf/explorer/interfaces index c1f2a57a..aeb55ed0 100755 --- a/cdist/conf/explorer/interfaces +++ b/cdist/conf/explorer/interfaces @@ -1,6 +1,6 @@ -#!/bin/sh +#!/bin/sh -e # -# 2012 Sébastien Gross +# 2019 Ander Punnar (ander-at-kvlt-dot-ee) # # This file is part of cdist. # @@ -17,35 +17,12 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# List all network interfaces in explorer/ifaces. One interface per line. -# -# If your OS is not supported please provide a ifconfig output -# -# Use ip, if available -if command -v ip >/dev/null; then - ip -o link show | sed -n 's/^[0-9]\+: \(.\+\): <.*/\1/p' - exit 0 -fi - -if ! command -v ifconfig >/dev/null; then - # no ifconfig, nothing we could do - exit 0 -fi - -uname_s="$(uname -s)" -REGEXP='s/^(.*)(:[[:space:]]*flags=|Link encap).*/\1/p' - -case "$uname_s" in - Darwin) - ifconfig -a | sed -n -E "$REGEXP" - ;; - Linux|*BSD) - ifconfig -a | sed -n -r "$REGEXP" - ;; - *) - echo "Unsupported ifconfig output for $uname_s" >&2 - exit 1 - ;; -esac +if command -v ip >/dev/null +then + ip -o link show | sed -n 's/^[0-9]\+: \(.\+\): <.*/\1/p' +elif command -v ifconfig >/dev/null +then + ifconfig -a | sed -n -E 's/^(.*)(:[[:space:]]*flags=|Link encap).*/\1/p' +fi \ + | sort -u diff --git a/cdist/conf/explorer/is-freebsd-jail b/cdist/conf/explorer/is-freebsd-jail old mode 100644 new mode 100755 index a6d11d1a..010917f5 --- a/cdist/conf/explorer/is-freebsd-jail +++ b/cdist/conf/explorer/is-freebsd-jail @@ -1 +1,2 @@ +#!/bin/sh sysctl -n security.jail.jailed 2>/dev/null | grep "1" || true diff --git a/cdist/conf/explorer/kernel_name b/cdist/conf/explorer/kernel_name old mode 100644 new mode 100755 index 98ebac2a..1f9cfca4 --- a/cdist/conf/explorer/kernel_name +++ b/cdist/conf/explorer/kernel_name @@ -1 +1,2 @@ +#!/bin/sh uname -s diff --git a/cdist/conf/explorer/lsb_codename b/cdist/conf/explorer/lsb_codename index eebd3e0f..26bb8e3d 100755 --- a/cdist/conf/explorer/lsb_codename +++ b/cdist/conf/explorer/lsb_codename @@ -20,8 +20,9 @@ # set +e -case "$($__explorer/os)" in +case "$("$__explorer/os")" in openwrt) + # shellcheck disable=SC1091 (. /etc/openwrt_release && echo "$DISTRIB_CODENAME") ;; *) diff --git a/cdist/conf/explorer/lsb_description b/cdist/conf/explorer/lsb_description index 23f45421..b1009627 100755 --- a/cdist/conf/explorer/lsb_description +++ b/cdist/conf/explorer/lsb_description @@ -20,8 +20,9 @@ # set +e -case "$($__explorer/os)" in +case "$("$__explorer/os")" in openwrt) + # shellcheck disable=SC1091 (. /etc/openwrt_release && echo "$DISTRIB_DESCRIPTION") ;; *) diff --git a/cdist/conf/explorer/lsb_id b/cdist/conf/explorer/lsb_id index 9754eb63..82ff9977 100755 --- a/cdist/conf/explorer/lsb_id +++ b/cdist/conf/explorer/lsb_id @@ -20,8 +20,9 @@ # set +e -case "$($__explorer/os)" in +case "$("$__explorer/os")" in openwrt) + # shellcheck disable=SC1091 (. /etc/openwrt_release && echo "$DISTRIB_ID") ;; *) diff --git a/cdist/conf/explorer/lsb_release b/cdist/conf/explorer/lsb_release index 35b5547c..5ebfff1a 100755 --- a/cdist/conf/explorer/lsb_release +++ b/cdist/conf/explorer/lsb_release @@ -20,8 +20,9 @@ # set +e -case "$($__explorer/os)" in +case "$("$__explorer/os")" in openwrt) + # shellcheck disable=SC1091 (. /etc/openwrt_release && echo "$DISTRIB_RELEASE") ;; *) diff --git a/cdist/conf/explorer/machine b/cdist/conf/explorer/machine index d4a0e106..7ecb67e3 100755 --- a/cdist/conf/explorer/machine +++ b/cdist/conf/explorer/machine @@ -22,6 +22,6 @@ # # -if command -v uname 2>&1 >/dev/null; then +if command -v uname >/dev/null 2>&1 ; then uname -m fi diff --git a/cdist/conf/explorer/machine_type b/cdist/conf/explorer/machine_type index 3b4f0308..1c84f4d7 100755 --- a/cdist/conf/explorer/machine_type +++ b/cdist/conf/explorer/machine_type @@ -2,6 +2,7 @@ # # 2014 Daniel Heule (hda at sfs.biz) # 2014 Thomas Oettli (otho at sfs.biz) +# 2020 Evilham (contact at evilham.com) # # This file is part of cdist. # @@ -18,63 +19,91 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# FIXME: other system types (not linux ...) +os=$("$__explorer/os") -if [ -d "/proc/vz" -a ! -d "/proc/bc" ]; then - echo openvz - exit -fi - -if [ -e "/proc/1/environ" ] && - cat "/proc/1/environ" | tr '\000' '\n' | grep -Eiq '^container='; then - echo lxc - exit -fi - -if [ -r /proc/cpuinfo ]; then - # this should only exist on virtual guest machines, - # tested on vmware, xen, kvm - if grep -q "hypervisor" /proc/cpuinfo; then - # this file is aviable in xen guest systems - if [ -r /sys/hypervisor/type ]; then - if grep -q -i "xen" /sys/hypervisor/type; then - echo virtual_by_xen - exit +vendor_string_to_machine_type() { + for vendor in vmware bochs kvm qemu virtualbox bhyve; do + if echo "${1}" | grep -q -i "${vendor}"; then + if [ "${vendor}" = "bochs" ] || [ "${vendor}" = "qemu" ]; then + vendor="kvm" fi - else - if [ -r /sys/class/dmi/id/product_name ]; then - if grep -q -i 'vmware' /sys/class/dmi/id/product_name; then - echo "virtual_by_vmware" - exit - elif grep -q -i 'bochs' /sys/class/dmi/id/product_name; then - echo "virtual_by_kvm" - exit - elif grep -q -i 'virtualbox' /sys/class/dmi/id/product_name; then - echo "virtual_by_virtualbox" - exit - fi - fi - - if [ -r /sys/class/dmi/id/sys_vendor ]; then - if grep -q -i 'qemu' /sys/class/dmi/id/sys_vendor; then - echo "virtual_by_kvm" - exit - fi - fi - - if [ -r /sys/class/dmi/id/chassis_vendor ]; then - if grep -q -i 'qemu' /sys/class/dmi/id/chassis_vendor; then - echo "virtual_by_kvm" - exit - fi - fi + echo "virtual_by_${vendor}" + exit fi - echo "virtual_by_unknown" - else - echo "physical" - fi -else - echo "unknown" -fi + done +} + +case "$os" in + "freebsd") + # FreeBSD does not have /proc/cpuinfo even when procfs is used. + # Instead there is a sysctl kern.vm_guest. + # Which is 'none' if physical, else the virtualisation. + vm_guest="$(sysctl -n kern.vm_guest 2>/dev/null || true)" + if [ -n "${vm_guest}" ]; then + if [ "${vm_guest}" = "none" ]; then + echo "physical" + exit + fi + echo "virtual_by_${vm_guest}" + exit + fi + ;; + + "openbsd") + # OpenBSD can also use the sysctl's: hw.vendor or hw.product. + # Note we can be reasonably sure about a machine being virtualised + # as long as we can identify the virtualisation technology. + # But not so much about it being physical... + # Patches are welcome / reach out if you have better ideas. + for sysctl in hw.vendor hw.product; do + # This exits if we can make a reasonable judgement + vendor_string_to_machine_type "$(sysctl -n "${sysctl}")" + done + ;; + + *) + # Defaulting to linux for compatibility with previous cdist behaviour + + if [ -d "/proc/vz" ] && [ ! -d "/proc/bc" ]; then + echo openvz + exit + fi + + if [ -e "/proc/1/environ" ] && + tr '\000' '\n' < "/proc/1/environ" | grep -Eiq '^container='; then + echo lxc + exit + fi + + if [ -r /proc/cpuinfo ]; then + # this should only exist on virtual guest machines, + # tested on vmware, xen, kvm, bhyve + if grep -q "hypervisor" /proc/cpuinfo; then + # this file is aviable in xen guest systems + if [ -r /sys/hypervisor/type ]; then + if grep -q -i "xen" /sys/hypervisor/type; then + echo virtual_by_xen + exit + fi + else + for vendor_file in /sys/class/dmi/id/product_name \ + /sys/class/dmi/id/sys_vendor \ + /sys/class/dmi/id/chasis_vendor; do + if [ -r ${vendor_file} ]; then + # This exits if we can make a reasonable judgement + vendor_string_to_machine_type "$(cat "${vendor_file}")" + fi + done + fi + echo "virtual_by_unknown" + exit + else + echo "physical" + exit + fi + fi + ;; +esac + +echo "unknown" diff --git a/cdist/conf/explorer/memory b/cdist/conf/explorer/memory index 4e3efff8..5ea15ada 100755 --- a/cdist/conf/explorer/memory +++ b/cdist/conf/explorer/memory @@ -29,7 +29,8 @@ case "$os" in echo "$(sysctl -n hw.memsize)/1024" | bc ;; - "openbsd") + *"bsd") + PATH=$(getconf PATH) echo "$(sysctl -n hw.physmem) / 1048576" | bc ;; diff --git a/cdist/conf/explorer/os b/cdist/conf/explorer/os index d522300c..46d87f3e 100755 --- a/cdist/conf/explorer/os +++ b/cdist/conf/explorer/os @@ -143,9 +143,16 @@ case "$uname_s" in esac if [ -f /etc/os-release ]; then + # after sles15, suse don't provide an /etc/SuSE-release anymore, but there is almost no difference between sles and opensuse leap, so call it suse + # shellcheck disable=SC1091 + if (. /etc/os-release && echo "${ID_LIKE}" | grep -q '\(^\|\ \)suse\($\|\ \)') + then + echo suse + exit 0 + fi # already lowercase, according to: # https://www.freedesktop.org/software/systemd/man/os-release.html - awk -F= '/^ID=/ {print $2;}' /etc/os-release + awk -F= '/^ID=/ { if ($2 ~ /^'"'"'(.*)'"'"'$/ || $2 ~ /^"(.*)"$/) { print substr($2, 2, length($2) - 2) } else { print $2 } }' /etc/os-release exit 0 fi diff --git a/cdist/conf/explorer/os_release b/cdist/conf/explorer/os_release new file mode 100644 index 00000000..6489446b --- /dev/null +++ b/cdist/conf/explorer/os_release @@ -0,0 +1,38 @@ +#!/bin/sh +# +# 2018 Adam Dej (dejko.a at gmail.com) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# + +# See os-release(5) and http://0pointer.de/blog/projects/os-release + +if test -f /etc/os-release +then + # Linux and FreeBSD (usually a symlink) + cat /etc/os-release +elif test -f /usr/lib/os-release +then + # systemd + cat /usr/lib/os-release +elif test -f /var/run/os-release +then + # FreeBSD (created by os-release service) + cat /var/run/os-release +fi + diff --git a/cdist/conf/explorer/os_version b/cdist/conf/explorer/os_version index 380782cc..3b02dedd 100755 --- a/cdist/conf/explorer/os_version +++ b/cdist/conf/explorer/os_version @@ -22,7 +22,7 @@ # # -case "$($__explorer/os)" in +case "$("$__explorer/os")" in amazon) cat /etc/system-release ;; @@ -31,7 +31,32 @@ case "$($__explorer/os)" in cat /etc/arch-release ;; debian) - cat /etc/debian_version + debian_version=$(cat /etc/debian_version) + case $debian_version + in + testing/unstable) + # previous to Debian 4.0 testing/unstable was used + # cf. https://metadata.ftp-master.debian.org/changelogs/main/b/base-files/base-files_11_changelog + echo 3.99 + ;; + */sid) + # sid versions don't have a number, so we decode by codename: + case $(expr "$debian_version" : '\([a-z]\{1,\}\)/') + in + bullseye) echo 10.99 ;; + buster) echo 9.99 ;; + stretch) echo 8.99 ;; + jessie) echo 7.99 ;; + wheezy) echo 6.99 ;; + squeeze) echo 5.99 ;; + lenny) echo 4.99 ;; + *) exit 1 + esac + ;; + *) + echo "$debian_version" + ;; + esac ;; devuan) cat /etc/devuan_version @@ -45,6 +70,11 @@ case "$($__explorer/os)" in macosx) sw_vers -productVersion ;; + freebsd) + # Apparently uname -r is not a reliable way to get the patch level. + # See: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=251743 + freebsd-version + ;; *bsd|solaris) uname -r ;; @@ -70,4 +100,7 @@ case "$($__explorer/os)" in ubuntu) lsb_release -sr ;; + alpine) + cat /etc/alpine-release + ;; esac diff --git a/cdist/conf/type/__acl/explorer/acl_is b/cdist/conf/type/__acl/explorer/acl_is new file mode 100755 index 00000000..a693c023 --- /dev/null +++ b/cdist/conf/type/__acl/explorer/acl_is @@ -0,0 +1,31 @@ +#!/bin/sh -e +# +# 2018 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +[ ! -e "/$__object_id" ] && exit 0 + +if ! command -v getfacl > /dev/null +then + echo 'getfacl not available' >&2 + exit 1 +fi + +getfacl "/$__object_id" 2>/dev/null \ + | grep -Eo '^(default:)?(user|group|(mask|other):):[^:][[:graph:]]+' \ + || true diff --git a/cdist/conf/type/__hostname/explorer/hostname_file b/cdist/conf/type/__acl/explorer/file_is similarity index 72% rename from cdist/conf/type/__hostname/explorer/hostname_file rename to cdist/conf/type/__acl/explorer/file_is index 6a00aa9f..096cffd1 100755 --- a/cdist/conf/type/__hostname/explorer/hostname_file +++ b/cdist/conf/type/__acl/explorer/file_is @@ -1,6 +1,6 @@ -#!/bin/sh +#!/bin/sh -e # -# 2014 Nico Schottelius (nico-cdist at schottelius.org) +# 2018 Ander Punnar (ander-at-kvlt-dot-ee) # # This file is part of cdist. # @@ -17,14 +17,15 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# Retrieve the contents of /etc/hostname -# -# Almost any distribution -if [ -f /etc/hostname ]; then - cat /etc/hostname -# SuSE -elif [ -f /etc/HOSTNAME ]; then - cat /etc/HOSTNAME +if [ -e "/$__object_id" ] +then + if [ -d "/$__object_id" ] + then echo directory + elif [ -f "/$__object_id" ] + then echo regular + else echo other + fi +else + echo missing fi diff --git a/cdist/conf/type/__acl/explorer/getent b/cdist/conf/type/__acl/explorer/getent new file mode 100755 index 00000000..7e6c2c30 --- /dev/null +++ b/cdist/conf/type/__acl/explorer/getent @@ -0,0 +1,4 @@ +#!/bin/sh -e + +getent passwd | awk -F: '{print "user:"$1}' +getent group | awk -F: '{print "group:"$1}' diff --git a/cdist/conf/type/__acl/gencode-remote b/cdist/conf/type/__acl/gencode-remote new file mode 100755 index 00000000..32318e91 --- /dev/null +++ b/cdist/conf/type/__acl/gencode-remote @@ -0,0 +1,143 @@ +#!/bin/sh -e +# +# 2018 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +file_is="$( cat "$__object/explorer/file_is" )" + +if [ "$file_is" = 'missing' ] \ + && [ -z "$__cdist_dry_run" ] \ + && [ ! -f "$__object/parameter/file" ] \ + && [ ! -f "$__object/parameter/directory" ] +then + exit 0 +fi + +os="$( cat "$__global/explorer/os" )" + +acl_path="/$__object_id" + +acl_is="$( cat "$__object/explorer/acl_is" )" + +if [ -f "$__object/parameter/source" ] +then + acl_source="$( cat "$__object/parameter/source" )" + + if [ "$acl_source" = '-' ] + then + acl_should="$( cat "$__object/stdin" )" + else + acl_should="$( grep -Ev '^#|^$' "$acl_source" )" + fi +elif [ -f "$__object/parameter/entry" ] +then + acl_should="$( cat "$__object/parameter/entry" )" +else + echo 'no parameters set' >&2 + exit 1 +fi + +# instead of setfacl's non-helpful message "Option -m: Invalid argument near character X" +# let's check if target has necessary users and groups, since mistyped or missing +# users/groups in target is most common reason. +echo "$acl_should" \ + | grep -Po '(user|group):[^:]+' \ + | sort -u \ + | while read -r l + do + if ! grep "$l" -Fxq "$__object/explorer/getent" + then + echo "no $l' in target" | sed "s/:/ '/" >&2 + exit 1 + fi + done + +if [ -f "$__object/parameter/default" ] +then + acl_should="$( echo "$acl_should" \ + | sed 's/^default://' \ + | sort -u \ + | sed 's/\(.*\)/default:\1\n\1/' )" +fi + +if [ "$file_is" = 'regular' ] \ + && echo "$acl_should" | grep -Eq '^default:' +then + # only directories can have default ACLs, + # but instead of error, + # let's just remove default entries + acl_should="$( echo "$acl_should" | grep -Ev '^default:' )" +fi + +if echo "$acl_should" | awk -F: '{ print $NF }' | grep -Fq 'X' +then + [ "$file_is" = 'directory' ] && rep=x || rep=- + + acl_should="$( echo "$acl_should" | sed "s/\\(.*\\)X/\\1$rep/" )" +fi + +setfacl_exec='setfacl' + +if [ -f "$__object/parameter/recursive" ] +then + if echo "$os" | grep -Fq 'freebsd' + then + echo "$os setfacl do not support recursive operations" >&2 + else + setfacl_exec="$setfacl_exec -R" + fi +fi + +if [ -f "$__object/parameter/remove" ] +then + echo "$acl_is" | while read -r acl + do + # skip wanted ACL entries which already exist + # and skip mask and other entries, because we + # can't actually remove them, but only change. + if echo "$acl_should" | grep -Eq "^$acl" \ + || echo "$acl" | grep -Eq '^(default:)?(mask|other)' + then continue + fi + + if echo "$os" | grep -Fq 'freebsd' + then + remove="$acl" + else + remove="$( echo "$acl" | sed 's/:...$//' )" + fi + + echo "$setfacl_exec -x \"$remove\" \"$acl_path\"" + echo "removed '$remove'" >> "$__messages_out" + done +fi + +for acl in $acl_should +do + if ! echo "$acl_is" | grep -Eq "^$acl" + then + if echo "$os" | grep -Fq 'freebsd' \ + && echo "$acl" | grep -Eq '^default:' + then + echo "setting default ACL in $os is currently not supported" >&2 + else + echo "$setfacl_exec -m \"$acl\" \"$acl_path\"" + echo "added '$acl'" >> "$__messages_out" + fi + fi +done diff --git a/cdist/conf/type/__acl/man.rst b/cdist/conf/type/__acl/man.rst new file mode 100644 index 00000000..307be72b --- /dev/null +++ b/cdist/conf/type/__acl/man.rst @@ -0,0 +1,108 @@ +cdist-type__acl(7) +================== + +NAME +---- +cdist-type__acl - Set ACL entries + + +DESCRIPTION +----------- +Fully supported and tested on Linux (ext4 filesystem), partial support for FreeBSD. + +See ``setfacl`` and ``acl`` manpages for more details. + +One of ``--entry`` or ``--source`` must be used. + + +OPTIONAL MULTIPLE PARAMETERS +---------------------------- +entry + Set ACL entry following ``getfacl`` output syntax. + Must be used if ``--source`` is not used. + + +OPTIONAL PARAMETERS +------------------- +source + Read ACL entries from stdin or file. + Ordering of entries is not important. + When reading from file, comments and empty lines are ignored. + Must be used if ``--entry`` is not used. + +file + Create/change file with ``__file`` using ``user:group:mode`` pattern. + +directory + Create/change directory with ``__directory`` using ``user:group:mode`` pattern. + + +BOOLEAN PARAMETERS +------------------ +default + Set all ACL entries as default too. + Only directories can have default ACLs. + Setting default ACL in FreeBSD is currently not supported. + +recursive + Make ``setfacl`` recursive (Linux only), but not ``getfacl`` in explorer. + +remove + Remove undefined ACL entries. + ``mask`` and ``other`` entries can't be removed, but only changed. + + +EXAMPLES +-------- + +.. code-block:: sh + + __acl /srv/project \ + --default \ + --recursive \ + --remove \ + --entry user:alice:rwx \ + --entry user:bob:r-x \ + --entry group:project-group:rwx \ + --entry group:some-other-group:r-x \ + --entry mask::r-x \ + --entry other::r-x + + # give Alice read-only access to subdir, + # but don't allow her to see parent content. + + __acl /srv/project2 \ + --remove \ + --entry default:group:secret-project:rwx \ + --entry group:secret-project:rwx \ + --entry user:alice:--x + + __acl /srv/project2/subdir \ + --default \ + --remove \ + --entry group:secret-project:rwx \ + --entry user:alice:r-x + + # read acl from stdin + echo 'user:alice:rwx' \ + | __acl /path/to/directory --source - + + # create/change directory too + __acl /path/to/directory \ + --default \ + --remove \ + --directory root:root:770 \ + --entry user:nobody:rwx + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2018 Ander Punnar. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__acl/manifest b/cdist/conf/type/__acl/manifest new file mode 100755 index 00000000..5fd23110 --- /dev/null +++ b/cdist/conf/type/__acl/manifest @@ -0,0 +1,11 @@ +#!/bin/sh -e + +for p in file directory +do + [ ! -f "$__object/parameter/$p" ] && continue + + "__$p" "/$__object_id" \ + --owner "$( awk -F: '{print $1}' "$__object/parameter/$p" )" \ + --group "$( awk -F: '{print $2}' "$__object/parameter/$p" )" \ + --mode "$( awk -F: '{print $3}' "$__object/parameter/$p" )" +done diff --git a/cdist/conf/type/__acl/parameter/boolean b/cdist/conf/type/__acl/parameter/boolean new file mode 100644 index 00000000..8b96693f --- /dev/null +++ b/cdist/conf/type/__acl/parameter/boolean @@ -0,0 +1,3 @@ +recursive +default +remove diff --git a/cdist/conf/type/__acl/parameter/optional b/cdist/conf/type/__acl/parameter/optional new file mode 100644 index 00000000..5a0c29a3 --- /dev/null +++ b/cdist/conf/type/__acl/parameter/optional @@ -0,0 +1,3 @@ +source +file +directory diff --git a/cdist/conf/type/__acl/parameter/optional_multiple b/cdist/conf/type/__acl/parameter/optional_multiple new file mode 100644 index 00000000..4c884f03 --- /dev/null +++ b/cdist/conf/type/__acl/parameter/optional_multiple @@ -0,0 +1 @@ +entry diff --git a/cdist/conf/type/__apt_backports/man.rst b/cdist/conf/type/__apt_backports/man.rst new file mode 100644 index 00000000..7036fb84 --- /dev/null +++ b/cdist/conf/type/__apt_backports/man.rst @@ -0,0 +1,104 @@ +cdist-type__debian_backports(7) +=============================== + +NAME +---- +cdist-type__apt_backports - Install backports + + +DESCRIPTION +----------- +This singleton type installs backports for the current OS release. +It aborts if backports are not supported for the specified OS or +no version codename could be fetched (like Debian unstable). + +The package index will be automatically updated if required. + +It supports backports from following OSes: + +- Debian +- Devuan +- Ubuntu + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +state + Represents the state of the backports repository. ``present`` or + ``absent``, defaults to ``present``. + + Will be directly passed to :strong:`cdist-type__apt_source`\ (7). + +mirror + The mirror to fetch the backports from. Will defaults to the generic + mirror of the current OS. + + Will be directly passed to :strong:`cdist-type__apt_source`\ (7). + + +BOOLEAN PARAMETERS +------------------ +None. + + +MESSAGES +-------- +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # setup the backports + __apt_backports + __apt_backports --state absent + __apt_backports --state present --mirror "http://ftp.de.debian.org/debian/" + + # install a backports package + # currently for the buster release backports + require="__apt_backports" __package_apt wireguard \ + --target-release buster-backports + + +ABORTS +------ +Aborts if the detected os is not Debian. + +Aborts if no distribuition codename could be detected. This is common for the +unstable distribution, but there is no backports repository for it already. + + +CAVEATS +------- +For Ubuntu, it setup all componenents for the backports repository: ``main``, +``restricted``, ``universe`` and ``multiverse``. The user may not want to +install proprietary packages, which will only be installed if the user +explicitly uses the backports target-release. The user may change this behavior +to install backports packages without the need of explicitly select it. + + +SEE ALSO +-------- +`Official Debian Backports site `_ + +:strong:`cdist-type__apt_source`\ (7) + + +AUTHORS +------- +Matthias Stecher + + +COPYING +------- +Copyright \(C) 2020 Matthias Stecher. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__apt_backports/manifest b/cdist/conf/type/__apt_backports/manifest new file mode 100755 index 00000000..bc47d8de --- /dev/null +++ b/cdist/conf/type/__apt_backports/manifest @@ -0,0 +1,81 @@ +#!/bin/sh -e +# __apt_backports/manifest +# +# 2020 Matthias Stecher (matthiasstecher at gmx.de) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Enables/disables backports repository. Utilises __apt_source for it. +# + + +# Get the distribution codename by /etc/os-release. +# is already executed in a subshell by string substitution +# lsb_release may not be given in all installations +codename_os_release() { + # shellcheck disable=SC1090 + . "$__global/explorer/os_release" + printf "%s" "$VERSION_CODENAME" +} + +# detect backport distribution +os="$(cat "$__global/explorer/os")" +case "$os" in + debian) + dist="$( codename_os_release )" + components="main" + mirror="http://deb.debian.org/debian/" + ;; + devuan) + dist="$( codename_os_release )" + components="main" + mirror="http://deb.devuan.org/merged" + ;; + ubuntu) + dist="$( codename_os_release )" + components="main restricted universe multiverse" + mirror="http://archive.ubuntu.com/ubuntu" + ;; + + *) + printf "Backports for %s are not supported!\n" "$os" >&2 + exit 1 + ;; +esac + +# error if no codename given (e.g. on Debian unstable) +if [ -z "$dist" ]; then + printf "No backports for unkown version of distribution %s!\n" "$os" >&2 + exit 1 +fi + + +# parameters +state="$(cat "$__object/parameter/state")" + +# mirror already set for the os, only override user-values +if [ -f "$__object/parameter/mirror" ]; then + mirror="$(cat "$__object/parameter/mirror")" +fi + + +# install the given backports repository +__apt_source "${dist}-backports" \ + --state "$state" \ + --distribution "${dist}-backports" \ + --component "$components" \ + --uri "$mirror" diff --git a/cdist/conf/type/__apt_backports/parameter/default/state b/cdist/conf/type/__apt_backports/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__apt_backports/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__apt_backports/parameter/optional b/cdist/conf/type/__apt_backports/parameter/optional new file mode 100644 index 00000000..4b05c235 --- /dev/null +++ b/cdist/conf/type/__apt_backports/parameter/optional @@ -0,0 +1,2 @@ +state +mirror diff --git a/cdist/conf/type/__pf_apply/singleton b/cdist/conf/type/__apt_backports/singleton similarity index 100% rename from cdist/conf/type/__pf_apply/singleton rename to cdist/conf/type/__apt_backports/singleton diff --git a/cdist/conf/type/__apt_key/explorer/state b/cdist/conf/type/__apt_key/explorer/state index f7940741..38f1bd3c 100755 --- a/cdist/conf/type/__apt_key/explorer/state +++ b/cdist/conf/type/__apt_key/explorer/state @@ -27,6 +27,18 @@ else keyid="$__object_id" fi -apt-key export "$keyid" | head -n 1 | grep -Fqe "BEGIN PGP PUBLIC KEY BLOCK" \ - && echo present \ - || echo absent +keydir="$(cat "$__object/parameter/keydir")" +keyfile="$keydir/$__object_id.gpg" + +if [ -d "$keydir" ] +then + if [ -f "$keyfile" ] + then echo present + else echo absent + fi +else + # fallback to deprecated apt-key + apt-key export "$keyid" | head -n 1 | grep -Fqe "BEGIN PGP PUBLIC KEY BLOCK" \ + && echo present \ + || echo absent +fi diff --git a/cdist/conf/type/__apt_key/gencode-remote b/cdist/conf/type/__apt_key/gencode-remote index 9c4fa00c..0c96ff67 100755 --- a/cdist/conf/type/__apt_key/gencode-remote +++ b/cdist/conf/type/__apt_key/gencode-remote @@ -31,12 +31,82 @@ if [ "$state_should" = "$state_is" ]; then exit 0 fi +keydir="$(cat "$__object/parameter/keydir")" +keyfile="$keydir/$__object_id.gpg" + case "$state_should" in present) keyserver="$(cat "$__object/parameter/keyserver")" - echo "apt-key adv --keyserver \"$keyserver\" --recv-keys \"$keyid\"" + + if [ -f "$__object/parameter/uri" ]; then + uri="$(cat "$__object/parameter/uri")" + + if [ -d "$keydir" ]; then + cat << EOF + +curl -s -L \\ + -o "$keyfile" \\ + "$uri" + +key="\$( cat "$keyfile" )" + +if echo "\$key" | grep -Fq 'BEGIN PGP PUBLIC KEY BLOCK' +then + echo "\$key" | gpg --dearmor > "$keyfile" +fi + +EOF + else + # fallback to deprecated apt-key + echo "curl -s -L '$uri' | apt-key add -" + fi + elif [ -d "$keydir" ]; then + # we need to kill gpg after 30 seconds, because gpg + # can get stuck if keyserver is not responding. + # exporting env var and not exit 1, + # because we need to clean up and kill dirmngr. + cat << EOF + +gpgtmphome="\$( mktemp -d )" + +if timeout 30s \\ + gpg --homedir "\$gpgtmphome" \\ + --keyserver "$keyserver" \\ + --recv-keys "$keyid" +then + gpg --homedir "\$gpgtmphome" \\ + --export "$keyid" \\ + > "$keyfile" +else + export GPG_GOT_STUCK=1 +fi + +GNUPGHOME="\$gpgtmphome" gpgconf --kill dirmngr + +rm -rf "\$gpgtmphome" + +if [ -n "\$GPG_GOT_STUCK" ] +then + echo "GPG GOT STUCK - no response from keyserver after 30 seconds" >&2 + exit 1 +fi + +EOF + else + # fallback to deprecated apt-key + echo "apt-key adv --keyserver \"$keyserver\" --recv-keys \"$keyid\"" + fi + + echo "added '$keyid'" >> "$__messages_out" ;; absent) - echo "apt-key del \"$keyid\"" + if [ -f "$keyfile" ]; then + echo "rm '$keyfile'" + else + # fallback to deprecated apt-key + echo "apt-key del \"$keyid\"" + fi + + echo "removed '$keyid'" >> "$__messages_out" ;; esac diff --git a/cdist/conf/type/__apt_key/man.rst b/cdist/conf/type/__apt_key/man.rst index 9009877e..234bc715 100644 --- a/cdist/conf/type/__apt_key/man.rst +++ b/cdist/conf/type/__apt_key/man.rst @@ -28,6 +28,12 @@ keyserver the keyserver from which to fetch the key. If omitted the default set in ./parameter/default/keyserver is used. +keydir + key save location, defaults to ``/etc/apt/trusted.pgp.d`` + +uri + the URI from which to download the key + EXAMPLES -------- @@ -47,15 +53,20 @@ EXAMPLES # same thing with other keyserver __apt_key UbuntuArchiveKey --keyid 437D05B5 --keyserver keyserver.ubuntu.com + # download key from the internet + __apt_key rabbitmq \ + --uri http://www.rabbitmq.com/rabbitmq-signing-key-public.asc + AUTHORS ------- Steven Armstrong +Ander Punnar COPYING ------- -Copyright \(C) 2011-2014 Steven Armstrong. You can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation, either version 3 of the +Copyright \(C) 2011-2019 Steven Armstrong and Ander Punnar. You can +redistribute it and/or modify it under the terms of the GNU General Public +License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/cdist/conf/type/__apt_key/manifest b/cdist/conf/type/__apt_key/manifest new file mode 100755 index 00000000..010357cd --- /dev/null +++ b/cdist/conf/type/__apt_key/manifest @@ -0,0 +1,8 @@ +#!/bin/sh -e + +__package gnupg + +if [ -f "$__object/parameter/uri" ] +then __package curl +else __package dirmngr +fi diff --git a/cdist/conf/type/__apt_key/parameter/default/keydir b/cdist/conf/type/__apt_key/parameter/default/keydir new file mode 100644 index 00000000..190eb2de --- /dev/null +++ b/cdist/conf/type/__apt_key/parameter/default/keydir @@ -0,0 +1 @@ +/etc/apt/trusted.gpg.d diff --git a/cdist/conf/type/__apt_key/parameter/optional b/cdist/conf/type/__apt_key/parameter/optional index 18cf2586..de647375 100644 --- a/cdist/conf/type/__apt_key/parameter/optional +++ b/cdist/conf/type/__apt_key/parameter/optional @@ -1,3 +1,5 @@ state keyid keyserver +keydir +uri diff --git a/cdist/conf/type/__apt_norecommends/man.rst b/cdist/conf/type/__apt_norecommends/man.rst index 001fffe4..9297b518 100644 --- a/cdist/conf/type/__apt_norecommends/man.rst +++ b/cdist/conf/type/__apt_norecommends/man.rst @@ -32,11 +32,12 @@ EXAMPLES AUTHORS ------- Steven Armstrong +Dennis Camera COPYING ------- -Copyright \(C) 2014 Steven Armstrong. You can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. +Copyright \(C) 2014 Steven Armstrong, 2020 Dennis Camera. +You can redistribute it and/or modify it under the terms of the GNU General +Public License as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. diff --git a/cdist/conf/type/__apt_norecommends/manifest b/cdist/conf/type/__apt_norecommends/manifest index e737df89..fc187784 100755 --- a/cdist/conf/type/__apt_norecommends/manifest +++ b/cdist/conf/type/__apt_norecommends/manifest @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,26 +20,28 @@ # -os=$(cat "$__global/explorer/os") +os=$(cat "${__global:?}/explorer/os") -case "$os" in - ubuntu|debian|devuan) - # No stinking recommends thank you very much. - # If I want something installed I will do so myself. - __file /etc/apt/apt.conf.d/99-no-recommends \ - --owner root --group root --mode 644 \ - --source - << DONE -APT::Install-Recommends "0"; -APT::Install-Suggests "0"; -APT::AutoRemove::RecommendsImportant "0"; -APT::AutoRemove::SuggestsImportant "0"; -DONE - ;; - *) - cat >&2 << DONE +case ${os} +in + (ubuntu|debian|devuan) + __file /etc/apt/apt.conf.d/00InstallRecommends --state present \ + --owner root --group root --mode 0644 --source - <<-'EOF' + APT::Install-Recommends "false"; + APT::Install-Suggests "false"; + APT::AutoRemove::RecommendsImportant "false"; + APT::AutoRemove::SuggestsImportant "false"; + EOF + + # TODO: Remove the following object after some time + require=__file/etc/apt/apt.conf.d/00InstallRecommends \ + __file /etc/apt/apt.conf.d/99-no-recommends --state absent + ;; + (*) + cat >&2 <. # -name="$__object_id" - __package software-properties-common require="__package/software-properties-common" \ diff --git a/cdist/conf/type/__apt_source/nonparallel b/cdist/conf/type/__apt_source/nonparallel new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__apt_unattended_upgrades/man.rst b/cdist/conf/type/__apt_unattended_upgrades/man.rst new file mode 100644 index 00000000..2231b5f9 --- /dev/null +++ b/cdist/conf/type/__apt_unattended_upgrades/man.rst @@ -0,0 +1,68 @@ +cdist-type__apt_unattended_upgrades(7) +====================================== + +NAME +---- +cdist-type__apt_unattended_upgrades - automatic installation of updates + + +DESCRIPTION +----------- + +Install and configure unattended-upgrades package. + +For more information see https://wiki.debian.org/UnattendedUpgrades. + + +OPTIONAL MULTIPLE PARAMETERS +---------------------------- +option + Set options for unattended-upgrades. See examples. + + Supported options with default values (as of 2020-01-17) are: + + - AutoFixInterruptedDpkg, default is "true" + - MinimalSteps, default is "true" + - InstallOnShutdown, default is "false" + - Mail, default is "" (empty) + - MailOnlyOnError, default is "false" + - Remove-Unused-Kernel-Packages, default is "true" + - Remove-New-Unused-Dependencies, default is "true" + - Remove-Unused-Dependencies, default is "false" + - Automatic-Reboot, default is "false" + - Automatic-Reboot-WithUsers, default is "true" + - Automatic-Reboot-Time, default is "02:00" + - SyslogEnable, default is "false" + - SyslogFacility, default is "daemon" + - OnlyOnACPower, default is "true" + - Skip-Updates-On-Metered-Connections, default is "true" + - Verbose, default is "false" + - Debug, default is "false" + +blacklist + Python regular expressions, matching packages to exclude from upgrading. + + +EXAMPLES +-------- + +.. code-block:: sh + + __apt_unattended_upgrades \ + --option Mail=root \ + --option MailOnlyOnError=true \ + --blacklist multipath-tools \ + --blacklist open-iscsi + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2020 Ander Punnar. You can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) any +later version. diff --git a/cdist/conf/type/__apt_unattended_upgrades/manifest b/cdist/conf/type/__apt_unattended_upgrades/manifest new file mode 100755 index 00000000..3c00e2f4 --- /dev/null +++ b/cdist/conf/type/__apt_unattended_upgrades/manifest @@ -0,0 +1,80 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +__package unattended-upgrades + +export require='__package/unattended-upgrades' + +# in normal circumstances 20auto-upgrades is managed +# by debconf and it can only contain these lines + +__file /etc/apt/apt.conf.d/20auto-upgrades \ + --owner root \ + --group root \ + --mode 644 \ + --source - << EOF +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; +EOF + +# lets not write into upstream 50unattended-upgrades file, +# but use our own config file to avoid clashes + +conf_file='/etc/apt/apt.conf.d/51unattended-upgrades-cdist' + +conf='# this file is managed by cdist' + +if [ -f "$__object/parameter/option" ] +then + o='' + + while read -r l + do + o="$( printf '%s\nUnattended-Upgrade::%s "%s";\n' "$o" "${l%%=*}" "${l#*=}" )" + done \ + < "$__object/parameter/option" + + conf="$( printf '%s\n%s\n' "$conf" "$o" )" +fi + +if [ -f "$__object/parameter/blacklist" ] +then + b='Unattended-Upgrade::Package-Blacklist {' + + while read -r l + do + b="$( printf '%s\n"%s";\n' "$b" "$l" )" + done \ + < "$__object/parameter/blacklist" + + conf="$( printf '%s\n%s\n}\n' "$conf" "$b" )" +fi + +if [ "$( echo "$conf" | wc -l )" -gt 1 ] +then + echo "$conf" \ + | __file "$conf_file" \ + --owner root \ + --group root \ + --mode 644 \ + --source - +else + __file "$conf_file" --state absent +fi diff --git a/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple b/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple new file mode 100644 index 00000000..ea4fba2b --- /dev/null +++ b/cdist/conf/type/__apt_unattended_upgrades/parameter/optional_multiple @@ -0,0 +1,2 @@ +option +blacklist diff --git a/cdist/conf/type/__apt_unattended_upgrades/singleton b/cdist/conf/type/__apt_unattended_upgrades/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__block/gencode-remote b/cdist/conf/type/__block/gencode-remote index f269c785..7a1f4064 100755 --- a/cdist/conf/type/__block/gencode-remote +++ b/cdist/conf/type/__block/gencode-remote @@ -18,6 +18,11 @@ # along with cdist. If not, see . # +# quote function from http://www.etalabs.net/sh_tricks.html +quote() { + printf '%s\n' "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" +} + file="$(cat "$__object/parameter/file" 2>/dev/null || echo "/$__object_id")" state_should=$(cat "$__object/parameter/state") prefix=$(cat "$__object/parameter/prefix" 2>/dev/null || echo "#cdist:__block/$__object_id") @@ -41,28 +46,29 @@ fi remove_block() { cat << DONE -tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX) +tmpfile=\$(mktemp ${quoted_file}.cdist.XXXXXXXXXX) # preserve ownership and permissions of existing file -if [ -f "$file" ]; then - cp -p "$file" "\$tmpfile" +if [ -f $quoted_file ]; then + cp -p $quoted_file "\$tmpfile" fi -awk -v prefix="^$prefix\$" -v suffix="^$suffix\$" ' +awk -v prefix=$(quote "$prefix") -v suffix=$(quote "$suffix") ' { - if (match(\$0,prefix)) { + if (\$0 == prefix) { triggered=1 } if (triggered) { - if (match(\$0,suffix)) { + if (\$0 == suffix) { triggered=0 } } else { print } -}' "$file" > "\$tmpfile" -mv -f "\$tmpfile" "$file" +}' $quoted_file > "\$tmpfile" +mv -f "\$tmpfile" $quoted_file DONE } +quoted_file="$(quote "$file")" case "$state_should" in present) if [ "$state_is" = "changed" ]; then @@ -72,7 +78,7 @@ case "$state_should" in echo add >> "$__messages_out" fi cat << DONE -cat >> "$file" << ${__type##*/}_DONE +cat >> $quoted_file << '${__type##*/}_DONE' $(cat "$block") ${__type##*/}_DONE DONE diff --git a/cdist/conf/type/__block/manifest b/cdist/conf/type/__block/manifest index 8fea3e83..726950d3 100755 --- a/cdist/conf/type/__block/manifest +++ b/cdist/conf/type/__block/manifest @@ -18,8 +18,6 @@ # along with cdist. If not, see . # - -file="$(cat "$__object/parameter/file" 2>/dev/null || echo "/$__object_id")" prefix=$(cat "$__object/parameter/prefix" 2>/dev/null || echo "#cdist:__block/$__object_id") suffix=$(cat "$__object/parameter/suffix" 2>/dev/null || echo "#/cdist:__block/$__object_id") text=$(cat "$__object/parameter/text") diff --git a/cdist/conf/type/__ccollect_source/gencode-remote b/cdist/conf/type/__ccollect_source/gencode-remote index 56003fef..57353c24 100755 --- a/cdist/conf/type/__ccollect_source/gencode-remote +++ b/cdist/conf/type/__ccollect_source/gencode-remote @@ -42,21 +42,20 @@ get_current_value() { } set_group() { - echo chgrp \"$1\" \"$destination\" - echo chgrp $1 >> "$__messages_out" + echo "chgrp '$1' '$destination'" + echo "chgrp '$1'" >> "$__messages_out" } set_owner() { - echo chown \"$1\" \"$destination\" - echo chown $1 >> "$__messages_out" + echo "chown '$1' '$destination'" + echo "chown '$1'" >> "$__messages_out" } set_mode() { - echo chmod \"$1\" \"$destination\" - echo chmod $1 >> "$__messages_out" + echo "chmod '$1' '$destination'" + echo "chmod '$1'" >> "$__messages_out" } -set_attributes= case "$state_should" in present|exists) # Note: Mode - needs to happen last as a chown/chgrp can alter mode by @@ -67,11 +66,11 @@ case "$state_should" in # change 0xxx format to xxx format => same as stat returns if [ "$attribute" = mode ]; then - value_should="$(echo $value_should | sed 's/^0\(...\)/\1/')" + value_should="$(echo "$value_should" | sed 's/^0\(...\)/\1/')" fi value_is="$(get_current_value "$attribute" "$value_should")" - if [ -f "$__object/files/set-attributes" -o "$value_should" != "$value_is" ]; then + if [ -f "$__object/files/set-attributes" ] || [ "$value_should" != "$value_is" ]; then "set_$attribute" "$value_should" fi fi @@ -81,7 +80,7 @@ case "$state_should" in absent) if [ "$type" = "file" ]; then - echo rm -f \"$destination\" + echo "rm -f '$destination'" echo remove >> "$__messages_out" fi ;; diff --git a/cdist/conf/type/__ccollect_source/manifest b/cdist/conf/type/__ccollect_source/manifest index 238c7e76..727a4c97 100755 --- a/cdist/conf/type/__ccollect_source/manifest +++ b/cdist/conf/type/__ccollect_source/manifest @@ -22,7 +22,7 @@ name="$__object_id" state="$(cat "$__object/parameter/state")" source="$(cat "$__object/parameter/source")" destination="$(cat "$__object/parameter/destination")" -ccollectconf="$(cat "$__object/parameter/ccollectconf" | sed 's,/$,,')" +ccollectconf="$(sed 's,/$,,' "$__object/parameter/ccollectconf")" sourcedir="$ccollectconf/sources" basedir="$sourcedir/$name" @@ -55,5 +55,5 @@ if [ -f "$__object/parameter/exclude" ]; then fi if [ -f "$__object/parameter/create-destination" ]; then - __directory "${destination}" --parents --state ${state} + __directory "${destination}" --parents --state "${state}" fi diff --git a/cdist/conf/type/__cdist/man.rst b/cdist/conf/type/__cdist/man.rst index 9e1c72cb..be082781 100644 --- a/cdist/conf/type/__cdist/man.rst +++ b/cdist/conf/type/__cdist/man.rst @@ -30,7 +30,7 @@ username source Select the source from which to clone cdist from. - Defaults to "git://github.com/ungleich/cdist.git". + Defaults to "git@code.ungleich.ch:ungleich-public/cdist.git". branch @@ -47,7 +47,7 @@ EXAMPLES __cdist /home/cdist/cdist # Use alternative source - __cdist --source "git://github.com/ungleich/cdist" /home/cdist/cdist + __cdist --source "git@code.ungleich.ch:ungleich-public/cdist.git" /home/cdist/cdist AUTHORS diff --git a/cdist/conf/type/__cdist/manifest b/cdist/conf/type/__cdist/manifest index a97cf288..0b0f1263 100755 --- a/cdist/conf/type/__cdist/manifest +++ b/cdist/conf/type/__cdist/manifest @@ -37,6 +37,7 @@ source="$(cat "$__object/parameter/source")" # out of it home=/home/$username +# shellcheck disable=SC2086 __user "$username" --home "$home" $shell require="__user/$username" __directory "$home" \ diff --git a/cdist/conf/type/__cdist/parameter/default/source b/cdist/conf/type/__cdist/parameter/default/source index 3f8e31ad..1ad3a250 100644 --- a/cdist/conf/type/__cdist/parameter/default/source +++ b/cdist/conf/type/__cdist/parameter/default/source @@ -1 +1 @@ -git://github.com/ungleich/cdist.git +git@code.ungleich.ch:ungleich-public/cdist.git diff --git a/cdist/conf/type/__check_messages/gencode-remote b/cdist/conf/type/__check_messages/gencode-remote new file mode 100755 index 00000000..ec36cecc --- /dev/null +++ b/cdist/conf/type/__check_messages/gencode-remote @@ -0,0 +1,26 @@ +#!/bin/sh -e +# +# 2019 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +if grep -Eq \ + "$( cat "$__object/parameter/pattern" )" \ + "$__messages_in" +then + tee "$__messages_out" < "$__object/parameter/execute" +fi diff --git a/cdist/conf/type/__check_messages/man.rst b/cdist/conf/type/__check_messages/man.rst new file mode 100644 index 00000000..5c80a0ae --- /dev/null +++ b/cdist/conf/type/__check_messages/man.rst @@ -0,0 +1,52 @@ +cdist-type__check_messages(7) +============================= + +NAME +---- +cdist-type__check_messages - Check messages for pattern and execute command on match. + + +DESCRIPTION +----------- +Check messages for pattern and execute command on match. + +This type is useful if you chain together multiple related types using +dependencies and want to restart service if at least one type changes +something. + +For more information about messages see `cdist messaging `_. + +For more information about dependencies and execution order see +`cdist manifest `_ documentation. + + +REQUIRED PARAMETERS +------------------- +pattern + Extended regular expression pattern for search (passed to ``grep -E``). + +execute + Command to execute on pattern match. + + +EXAMPLES +-------- + +.. code-block:: sh + + __check_messages munin \ + --pattern '^__(file|link|line)/etc/munin/' \ + --execute 'service munin-node restart' + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2019 Ander Punnar. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__check_messages/parameter/required b/cdist/conf/type/__check_messages/parameter/required new file mode 100644 index 00000000..374363cb --- /dev/null +++ b/cdist/conf/type/__check_messages/parameter/required @@ -0,0 +1,2 @@ +pattern +execute diff --git a/cdist/conf/type/__clean_path/explorer/list b/cdist/conf/type/__clean_path/explorer/list new file mode 100755 index 00000000..2bdc63a5 --- /dev/null +++ b/cdist/conf/type/__clean_path/explorer/list @@ -0,0 +1,40 @@ +#!/bin/sh -e +# +# 2019 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +if [ -f "$__object/parameter/path" ] +then + path="$( cat "$__object/parameter/path" )" +else + path="/$__object_id" +fi + +[ ! -d "$path" ] && exit 0 + +pattern="$( cat "$__object/parameter/pattern" )" + +if [ -f "$__object/parameter/exclude" ] +then + exclude="$( cat "$__object/parameter/exclude" )" + + find "$path" -mindepth 1 -maxdepth 1 -regex "$pattern" \ + -and -not -regex "$exclude" +else + find "$path" -mindepth 1 -maxdepth 1 -regex "$pattern" +fi diff --git a/cdist/conf/type/__clean_path/gencode-remote b/cdist/conf/type/__clean_path/gencode-remote new file mode 100755 index 00000000..2899c4a5 --- /dev/null +++ b/cdist/conf/type/__clean_path/gencode-remote @@ -0,0 +1,53 @@ +#!/bin/sh -e +# +# 2019 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +[ ! -s "$__object/explorer/list" ] && exit 0 + +if [ -f "$__object/parameter/path" ] +then + path="$( cat "$__object/parameter/path" )" +else + path="/$__object_id" +fi + +pattern="$( cat "$__object/parameter/pattern" )" + +if [ -f "$__object/parameter/exclude" ] +then + exclude="$( cat "$__object/parameter/exclude" )" + + echo "find '$path' -mindepth 1 -maxdepth 1 -regex '$pattern'" \ + "-and -not -regex '$exclude'" \ + '-exec rm -rf {} \;' +else + echo "find '$path' -mindepth 1 -maxdepth 1 -regex '$pattern'" \ + '-exec rm -rf {} \;' +fi + +while read -r f +do + echo "removed '$f'" >> "$__messages_out" +done \ +< "$__object/explorer/list" + +if [ -f "$__object/parameter/onchange" ] +then + cat "$__object/parameter/onchange" +fi diff --git a/cdist/conf/type/__clean_path/man.rst b/cdist/conf/type/__clean_path/man.rst new file mode 100644 index 00000000..31d90701 --- /dev/null +++ b/cdist/conf/type/__clean_path/man.rst @@ -0,0 +1,68 @@ +cdist-type__clean_path(7) +========================= + +NAME +---- +cdist-type__clean_path - Remove files and directories which match the pattern. + + +DESCRIPTION +----------- +Remove files and directories which match the pattern. + +Provided path must be a directory. + +Patterns are passed to ``find``'s ``-regex`` - see ``find(1)`` for more details. + +Look up of files and directories is non-recursive (``-maxdepth 1``). + +Parent directory is excluded (``-mindepth 1``). + +This type is not POSIX compatible (sorry, Solaris users). + + +REQUIRED PARAMETERS +------------------- +pattern + Pattern of files which are removed from path. + + +OPTIONAL PARAMETERS +------------------- +path + Path which will be cleaned. Defaults to ``$__object_id``. + +exclude + Pattern of files which are excluded from removal. + +onchange + The code to run if files or directories were removed. + + +EXAMPLES +-------- + +.. code-block:: sh + + __clean_path /etc/apache2/conf-enabled \ + --pattern '.+' \ + --exclude '.+\(charset\.conf\|security\.conf\)' \ + --onchange 'service apache2 restart' + + __clean_path apache2-conf-enabled \ + --path /etc/apache2/conf-enabled \ + --pattern '.+' \ + --exclude '.+\(charset\.conf\|security\.conf\)' \ + --onchange 'service apache2 restart' + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2019 Ander Punnar. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__clean_path/parameter/optional b/cdist/conf/type/__clean_path/parameter/optional new file mode 100644 index 00000000..3b97f71c --- /dev/null +++ b/cdist/conf/type/__clean_path/parameter/optional @@ -0,0 +1,3 @@ +exclude +onchange +path diff --git a/cdist/conf/type/__clean_path/parameter/required b/cdist/conf/type/__clean_path/parameter/required new file mode 100644 index 00000000..54774947 --- /dev/null +++ b/cdist/conf/type/__clean_path/parameter/required @@ -0,0 +1 @@ +pattern diff --git a/cdist/conf/type/__config_file/manifest b/cdist/conf/type/__config_file/manifest index 3155f79b..be8f9f67 100755 --- a/cdist/conf/type/__config_file/manifest +++ b/cdist/conf/type/__config_file/manifest @@ -19,7 +19,8 @@ # set -- "/${__object_id}" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in source) source="$(cat "$__object/parameter/source")" diff --git a/cdist/conf/type/__consul/files/versions/1.2.3/cksum b/cdist/conf/type/__consul/files/versions/1.2.3/cksum new file mode 100644 index 00000000..6352409e --- /dev/null +++ b/cdist/conf/type/__consul/files/versions/1.2.3/cksum @@ -0,0 +1 @@ +191982 110369685 diff --git a/cdist/conf/type/__consul/files/versions/1.2.3/source b/cdist/conf/type/__consul/files/versions/1.2.3/source new file mode 100644 index 00000000..5e67bc37 --- /dev/null +++ b/cdist/conf/type/__consul/files/versions/1.2.3/source @@ -0,0 +1 @@ +https://releases.hashicorp.com/consul/1.2.3/consul_1.2.3_linux_amd64.zip diff --git a/cdist/conf/type/__consul/files/versions/1.3.0/cksum b/cdist/conf/type/__consul/files/versions/1.3.0/cksum new file mode 100644 index 00000000..7a885378 --- /dev/null +++ b/cdist/conf/type/__consul/files/versions/1.3.0/cksum @@ -0,0 +1 @@ +1714523667 98363467 consul diff --git a/cdist/conf/type/__consul/files/versions/1.3.0/source b/cdist/conf/type/__consul/files/versions/1.3.0/source new file mode 100644 index 00000000..18a1ba8e --- /dev/null +++ b/cdist/conf/type/__consul/files/versions/1.3.0/source @@ -0,0 +1 @@ +https://releases.hashicorp.com/consul/1.3.0/consul_1.3.0_linux_amd64.zip diff --git a/cdist/conf/type/__consul/files/versions/1.5.0/cksum b/cdist/conf/type/__consul/files/versions/1.5.0/cksum new file mode 100644 index 00000000..efca9caa --- /dev/null +++ b/cdist/conf/type/__consul/files/versions/1.5.0/cksum @@ -0,0 +1 @@ +886614099 103959898 consul diff --git a/cdist/conf/type/__consul/files/versions/1.5.0/source b/cdist/conf/type/__consul/files/versions/1.5.0/source new file mode 100644 index 00000000..cafa9248 --- /dev/null +++ b/cdist/conf/type/__consul/files/versions/1.5.0/source @@ -0,0 +1 @@ +https://releases.hashicorp.com/consul/1.5.0/consul_1.5.0_linux_amd64.zip diff --git a/cdist/conf/type/__consul/gencode-remote b/cdist/conf/type/__consul/gencode-remote index 22e9eea1..2a21054f 100755 --- a/cdist/conf/type/__consul/gencode-remote +++ b/cdist/conf/type/__consul/gencode-remote @@ -39,10 +39,10 @@ version_dir="$versions_dir/$version" source=$(cat "$version_dir/source") source_file_name="${source##*/}" -cksum_should=$(cat "$version_dir/cksum" | cut -d' ' -f1,2) +cksum_should=$(cut -d' ' -f1,2 "$version_dir/cksum") cat << eof - tmpdir=\$(mktemp -d --tmpdir="/tmp" "${__type##*/}.XXXXXXXXXX") + tmpdir=\$(mktemp -d -p /tmp "${__type##*/}.XXXXXXXXXX") curl -s -L "$source" > "\$tmpdir/$source_file_name" unzip -p "\$tmpdir/$source_file_name" > "${destination}.tmp" rm -rf "\$tmpdir" diff --git a/cdist/conf/type/__consul/manifest b/cdist/conf/type/__consul/manifest index 0dd50f53..156eb667 100755 --- a/cdist/conf/type/__consul/manifest +++ b/cdist/conf/type/__consul/manifest @@ -24,7 +24,7 @@ os=$(cat "$__global/explorer/os") case "$os" in - scientific|centos|redhat|ubuntu|debian|devuan|archlinux|gentoo) + alpine|scientific|centos|redhat|ubuntu|debian|devuan|archlinux|gentoo) # any linux should work : ;; @@ -47,6 +47,7 @@ fi if [ -f "$__object/parameter/direct" ]; then __package unzip + __package curl else __staged_file /usr/local/bin/consul \ --source "$(cat "$version_dir/source")" \ diff --git a/cdist/conf/type/__consul_agent/files/consul.sys-openrc b/cdist/conf/type/__consul_agent/files/consul.sys-openrc new file mode 100644 index 00000000..1dbe9375 --- /dev/null +++ b/cdist/conf/type/__consul_agent/files/consul.sys-openrc @@ -0,0 +1,38 @@ +#!/sbin/openrc-run +# 2019 Nico Schottelius (nico-cdist at schottelius.org) + +description="consul agent" + +pidfile="${CONSUL_PIDFILE:-"/var/run/$RC_SVCNAME/pidfile"}" +command="${CONSUL_BINARY:-"/usr/local/bin/consul"}" + + +checkconfig() { + if [ ! -d /var/run/consul ] ; then + mkdir -p /var/run/consul || return 1 + chown consul:consul /var/run/$NAME || return 1 + chmod 2770 /var/run/$NAME || return 1 + fi +} + +start() { + need net + + start-stop-daemon --start --quiet --oknodo \ + --pidfile "$pidfile" --background \ + --exec $command -- agent -pid-file="$pidfile" -config-dir /etc/consul/conf.d +} +start_pre() { + checkconfig +} + +stop() { + if [ "${RC_CMD}" = "restart" ] ; then + checkconfig || return 1 + fi + + ebegin "Stopping $RC_SVCNAME" + start-stop-daemon --stop --exec "$command" \ + --pidfile "$pidfile" --quiet + eend $? +} diff --git a/cdist/conf/type/__consul_agent/files/consul.sysv-debian b/cdist/conf/type/__consul_agent/files/consul.sysv-debian index a75c555d..4f43c000 100644 --- a/cdist/conf/type/__consul_agent/files/consul.sysv-debian +++ b/cdist/conf/type/__consul_agent/files/consul.sysv-debian @@ -1,6 +1,6 @@ #!/bin/sh # -# 2015 Nico Schottelius (nico-cdist at schottelius.org) +# 2015-2018 Nico Schottelius (nico-cdist at schottelius.org) # 2015 Steven Armstrong (steven-cdist at armstrong.cc) # # This file is part of cdist. @@ -18,11 +18,24 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # +### BEGIN INIT INFO +# Provides: consul +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $local_fs +# Should-Start: +# Should-Stop: +# Short-Description: consul +# Description: consul agent +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +### END INIT INFO if [ -f "/etc/default/consul" ]; then + # shellcheck disable=SC1091 . /etc/default/consul fi +# shellcheck disable=SC1091 . /lib/lsb/init-functions NAME=consul diff --git a/cdist/conf/type/__consul_agent/files/consul.sysv-redhat b/cdist/conf/type/__consul_agent/files/consul.sysv-redhat index 13dafd2e..58fc9bd9 100644 --- a/cdist/conf/type/__consul_agent/files/consul.sysv-redhat +++ b/cdist/conf/type/__consul_agent/files/consul.sysv-redhat @@ -11,49 +11,52 @@ # pidfile: /var/run/consul/pidfile # Source function library. + +# shellcheck disable=SC1091 . /etc/init.d/functions NAME=consul CONSUL=/usr/local/bin/consul -CONFIG=/etc/$NAME/conf.d -PID_FILE=/var/run/$NAME/pidfile -LOG_FILE=/var/log/$NAME +CONFIG="/etc/$NAME/conf.d" +PID_FILE="/var/run/$NAME/pidfile" +LOG_FILE="/var/log/$NAME" -[ -e /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME -export GOMAXPROCS=${GOMAXPROCS:-2} +# shellcheck disable=SC1090 +[ -e "/etc/sysconfig/$NAME" ] && . "/etc/sysconfig/$NAME" +export GOMAXPROCS="${GOMAXPROCS:-2}" -mkdir -p /var/run/$NAME -chown consul:consul /var/run/$NAME -chmod 2770 /var/run/$NAME +mkdir -p "/var/run/$NAME" +chown consul:consul "/var/run/$NAME" +chmod 2770 "/var/run/$NAME" start() { - echo -n "Starting $NAME: " + printf "Starting %s: " "$NAME" daemon --user=consul \ --pidfile="$PID_FILE" \ "$CONSUL" agent -pid-file="$PID_FILE" -config-dir "$CONFIG" >> "$LOG_FILE" & retcode=$? - touch /var/lock/subsys/$NAME - return $retcode + touch "/var/lock/subsys/$NAME" + return "$retcode" } stop() { - echo -n "Shutting down $NAME: " - killproc -p "$PID_FILE" $NAME + printf "Shutting down %s: " "$NAME" + killproc -p "$PID_FILE" "$NAME" retcode=$? - rm -f /var/lock/subsys/$NAME - return $retcode + rm -f "/var/lock/subsys/$NAME" + return "$retcode" } case "$1" in start) - if $(status -p "$PID_FILE" $NAME >/dev/null); then + if status -p "$PID_FILE" "$NAME" >/dev/null; then echo "$NAME already running" else start fi ;; stop) - if $(status -p "$PID_FILE" $NAME >/dev/null); then + if status -p "$PID_FILE" "$NAME" >/dev/null; then stop else echo "$NAME not running" @@ -63,25 +66,25 @@ case "$1" in "$CONSUL" info ;; status) - status -p "$PID_FILE" $NAME + status -p "$PID_FILE" "$NAME" exit $? ;; restart) - if $(status -p "$PID_FILE" $NAME >/dev/null); then + if status -p "$PID_FILE" "$NAME" >/dev/null; then stop fi start ;; reload) - if $(status -p "$PID_FILE" $NAME >/dev/null); then - kill -HUP `cat $PID_FILE` + if status -p "$PID_FILE" "$NAME" >/dev/null; then + kill -HUP "$(cat "$PID_FILE")" else echo "$NAME not running" fi ;; condrestart) - if [ -f /var/lock/subsys/$NAME ]; then - if $(status -p "$PID_FILE" $NAME >/dev/null); then + if [ -f "/var/lock/subsys/$NAME" ]; then + if status -p "$PID_FILE" "$NAME" >/dev/null; then stop fi start diff --git a/cdist/conf/type/__consul_agent/man.rst b/cdist/conf/type/__consul_agent/man.rst index 966abc60..62ee70bb 100644 --- a/cdist/conf/type/__consul_agent/man.rst +++ b/cdist/conf/type/__consul_agent/man.rst @@ -116,6 +116,9 @@ verify-incoming verify-outgoing enforce the use of TLS and verify the peers authenticity on outgoing connections +use-distribution-package + uses distribution package instead of upstream binary + EXAMPLES -------- diff --git a/cdist/conf/type/__consul_agent/manifest b/cdist/conf/type/__consul_agent/manifest index 820018c9..7b54529c 100755 --- a/cdist/conf/type/__consul_agent/manifest +++ b/cdist/conf/type/__consul_agent/manifest @@ -1,7 +1,8 @@ #!/bin/sh -e # # 2015 Steven Armstrong (steven-cdist at armstrong.cc) -# 2015 Nico Schottelius (nico-cdist at schottelius.org) +# 2015-2020 Nico Schottelius (nico-cdist at schottelius.org) +# 2019 Timothée Floure (timothee.floure at ungleich.ch) # # This file is part of cdist. # @@ -19,132 +20,87 @@ # along with cdist. If not, see . # - os=$(cat "$__global/explorer/os") -case "$os" in - scientific|centos|debian|devuan|redhat|ubuntu) - # whitelist safeguard - : - ;; - *) - echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2 - echo "Please contribute an implementation for it if you can." >&2 - exit 1 - ;; -esac +### +# Type parameters. state="$(cat "$__object/parameter/state")" user="$(cat "$__object/parameter/user")" group="$(cat "$__object/parameter/group")" -data_dir="/var/lib/consul" -conf_dir="/etc/consul/conf.d" -conf_file="config.json" +release=$(cat "$__global/explorer/lsb_release") +if [ -f "$__object/parameter/use-distribution-package" ]; then + use_distribution_package=1 +fi -# FIXME: there has got to be a better way to handle the dependencies in this case -case "$state" in - present) - __group "$group" --system --state "$state" - require="__group/$group" \ - __user "$user" --system --gid "$group" \ - --home "$data_dir" --state "$state" - export require="__user/consul" - ;; - absent) - echo "Sorry, state=absent currently not supported :-(" >&2 - exit 1 - require="$__object_name" \ - __user "$user" --system --gid "$group" --state "$state" - require="__user/$user" \ - __group "$group" --system --state "$state" - ;; +### +# Those are default that might be overriden by os-specific logic. + +data_dir="/var/lib/consul" + + + +tls_dir="$conf_dir/tls" + +case "$os" in + alpine) + conf_dir="/etc/consul" + conf_file="server.json" + ;; + *) + conf_dir="/etc/consul/conf.d" + conf_file="config.json" + ;; esac -__directory /etc/consul \ - --owner root --group "$group" --mode 750 --state "$state" -require="__directory/etc/consul" \ - __directory "$conf_dir" \ - --owner root --group "$group" --mode 750 --state "$state" +### +# Sane deployment, based on distribution package when available. -if [ -f "$__object/parameter/ca-file-source" -o -f "$__object/parameter/cert-file-source" -o -f "$__object/parameter/key-file-source" ]; then - # create directory for ssl certs - require="__directory/etc/consul" \ - __directory /etc/consul/ssl \ - --owner root --group "$group" --mode 750 --state "$state" -fi +distribution_setup () { + case "$os" in + debian) + # consul is only available starting Debian 10 (buster). + # See https://packages.debian.org/buster/consul + if [ "$release" -lt 10 ]; then + echo "Consul is not available for your debian release." >&2 + echo "Please use the 'manual' (i.e. non-package) installation or \ + upgrade the target system." >&2 + exit 1 + fi -__directory "$data_dir" \ - --owner "$user" --group "$group" --mode 770 --state "$state" + # Override previously defined environment to match debian packaging. + conf_dir='/etc/consul.d' + user='consul' + group='consul' + ;; + alpine) + # consul is only available starting Alpine 3.12 (= edge during the 3.11 cycle). + # See https://pkgs.alpinelinux.org/packages?name=consul&branch=edge + # Override previously defined environment to match alpine packaging. + conf_dir='/etc/consul' + conf_file='server.json' + data_dir='/var/consul' + user='consul' + group='consul' + ;; + *) + echo "Your operating system ($os) is currently not supported with the \ + --use-distribution-package flag (${__type##*/})." >&2 + echo "Please use non-package installation or contribute an \ + implementation for if you can." >&2 + exit 1 + ;; + esac -# Generate json config file -( -echo "{" + # Install consul package. + __package consul --state "$state" -# parameters we define ourself -printf ' "data_dir": "%s"\n' "$data_dir" + export config_deployment_requires="__package/consul" +} -for param in $(ls "$__object/parameter/"); do - case "$param" in - state|user|group|json-config) continue ;; - ca-file-source|cert-file-source|key-file-source) - source="$(cat "$__object/parameter/$param")" - destination="/etc/consul/ssl/${source##*/}" - require="__directory/etc/consul/ssl" \ - __file "$destination" \ - --owner root --group consul --mode 640 \ - --source "$source" \ - --state "$state" - key="$(echo "${param%-*}" | tr '-' '_')" - printf ' ,"%s": "%s"\n' "$key" "$destination" - ;; - disable-remote-exec|disable-update-check|leave-on-terminate|rejoin-after-leave|server|enable-syslog|verify-incoming|verify-outgoing) - # handle boolean parameters - key="$(echo "$param" | tr '-' '_')" - printf ' ,"%s": true\n' "$key" - ;; - retry-join) - # join multiple parameters into json array - retry_join="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join")" - # remove trailing , - printf ' ,"retry_join": [%s]\n' "${retry_join%*,}" - ;; - retry-join-wan) - # join multiple parameters into json array over wan - retry_join_wan="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join-wan")" - # remove trailing , - printf ' ,"retry_join_wan": [%s]\n' "${retry_join_wan%*,}" - ;; - bootstrap-expect) - # integer key=value parameters - key="$(echo "$param" | tr '-' '_')" - printf ' ,"%s": %s\n' "$key" "$(cat "$__object/parameter/$param")" - ;; - *) - # string key=value parameters - key="$(echo "$param" | tr '-' '_')" - printf ' ,"%s": "%s"\n' "$key" "$(cat "$__object/parameter/$param")" - ;; - esac -done -if [ -f "$__object/parameter/json-config" ]; then - json_config="$(cat "$__object/parameter/json-config")" - if [ "$json_config" = "-" ]; then - json_config="$__object/stdin" - fi - # remove leading and trailing whitespace and commas from first and last line - # indent each line with 3 spaces for consistency - json=$(sed -e 's/^[ \t]*/ /' -e '1s/^[ \t,]*//' -e '$s/[ \t,]*$//' "$json_config") - printf ' ,%s\n' "$json" -fi -echo "}" -) | \ -require="__directory${conf_dir}" \ - __config_file "${conf_dir}/${conf_file}" \ - --owner root --group "$group" --mode 640 \ - --state "$state" \ - --onchange 'service consul status >/dev/null && service consul reload || true' \ - --source - +### +# LEGACY manual deployment, kept for compatibility reasons. init_sysvinit() { @@ -178,48 +134,186 @@ init_upstart() require="__file/etc/init/consul.conf" __start_on_boot consul } -# Install init script to start on boot -case "$os" in - centos|redhat) - os_version="$(sed 's/[^0-9.]//g' "$__global/explorer/os_version")" - major_version="${os_version%%.*}" - case "$major_version" in - [456]) - init_sysvinit redhat - ;; - 7) - init_systemd - ;; - *) - echo "Unsupported CentOS/Redhat version: $os_version" >&2 - exit 1 - ;; - esac - ;; +manual_setup () { + case "$os" in + alpine|scientific|centos|debian|devuan|redhat|ubuntu) + # whitelist safeguard + : + ;; + *) + echo "Your operating system ($os) is currently not supported by this \ + type (${__type##*/})." >&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 + ;; + esac - debian) - os_version=$(cat "$__global/explorer/os_version") - major_version="${os_version%%.*}" + # FIXME: there has got to be a better way to handle the dependencies in this case + case "$state" in + present) + __group "$group" --system --state "$state" + require="__group/$group" __user "$user" \ + --system --gid "$group" --home "$data_dir" --state "$state" + ;; + *) + echo "The $state state is not (yet?) supported by this type." >&2 + exit 1 + ;; + esac - case "$major_version" in - [567]) - init_sysvinit debian - ;; - 8) - init_systemd - ;; - *) - echo "Unsupported Debian version $os_version" >&2 - exit 1 - ;; - esac - ;; + # Create data directory. + require="__user/consul" __directory "$data_dir" \ + --owner "$user" --group "$group" --mode 770 --state "$state" - devuan) - init_sysvinit debian - ;; + # Create config directory. + require="__user/consul" __directory "$conf_dir" \ + --parents --owner root --group "$group" --mode 750 --state "$state" - ubuntu) - init_upstart - ;; -esac + # Install init script to start on boot + case "$os" in + devuan) + init_sysvinit debian + ;; + centos|redhat) + os_version="$(sed 's/[^0-9.]//g' "$__global/explorer/os_version")" + major_version="${os_version%%.*}" + case "$major_version" in + [456]) + init_sysvinit redhat + ;; + 7) + init_systemd + ;; + *) + echo "Unsupported CentOS/Redhat version: $os_version" >&2 + exit 1 + ;; + esac + ;; + + debian) + os_version=$(cat "$__global/explorer/os_version") + major_version="${os_version%%.*}" + + case "$major_version" in + [567]) + init_sysvinit debian + ;; + [89]|10) + init_systemd + ;; + *) + echo "Unsupported Debian version $os_version" >&2 + exit 1 + ;; + esac + ;; + + ubuntu) + init_upstart + ;; + esac + + config_deployment_requires="__user/consul __directory/$conf_dir" +} + +### +# Trigger requested installation method. +if [ $use_distribution_package ]; then + distribution_setup +else + manual_setup +fi + +### +# Install TLS certificates. + +if [ -f "$__object/parameter/ca-file-source" ] || \ + [ -f "$__object/parameter/cert-file-source" ] || \ + [ -f "$__object/parameter/key-file-source" ]; then + + requires="$config_deployment_requires" __directory "$tls_dir" \ + --owner root --group "$group" --mode 750 --state "$state" + + # Append to service restart requirements. + restart_requires="$restart_requires __directory/$conf_dir/tls" +fi + +### +# Generate and deploy configuration. + +json_configuration=$( + echo "{" + + # parameters we define ourself + printf ' "data_dir": "%s"\n' "$data_dir" + + cd "$__object/parameter/" + for param in *; do + case "$param" in + state|user|group|json-config|use-distribution-package) continue ;; + ca-file-source|cert-file-source|key-file-source) + source="$(cat "$__object/parameter/$param")" + destination="$tls_dir/${source##*/}" + require="__directory/$tls_dir" \ + __file "$destination" \ + --owner root --group consul --mode 640 \ + --source "$source" \ + --state "$state" + key="$(echo "${param%-*}" | tr '-' '_')" + printf ' ,"%s": "%s"\n' "$key" "$destination" + ;; + disable-remote-exec|disable-update-check|leave-on-terminate\ + |rejoin-after-leave|server|enable-syslog|verify-incoming|verify-outgoing) + # handle boolean parameters + key="$(echo "$param" | tr '-' '_')" + printf ' ,"%s": true\n' "$key" + ;; + retry-join) + # join multiple parameters into json array + retry_join="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join")" + # remove trailing , + printf ' ,"retry_join": [%s]\n' "${retry_join%*,}" + ;; + retry-join-wan) + # join multiple parameters into json array over wan + retry_join_wan="$(awk '{printf "\""$1"\","}' "$__object/parameter/retry-join-wan")" + # remove trailing , + printf ' ,"retry_join_wan": [%s]\n' "${retry_join_wan%*,}" + ;; + bootstrap-expect) + # integer key=value parameters + key="$(echo "$param" | tr '-' '_')" + printf ' ,"%s": %s\n' "$key" "$(cat "$__object/parameter/$param")" + ;; + *) + # string key=value parameters + key="$(echo "$param" | tr '-' '_')" + printf ' ,"%s": "%s"\n' "$key" "$(cat "$__object/parameter/$param")" + ;; + esac + done + if [ -f "$__object/parameter/json-config" ]; then + json_config="$(cat "$__object/parameter/json-config")" + if [ "$json_config" = "-" ]; then + json_config="$__object/stdin" + fi + # remove leading and trailing whitespace and commas from first and last line + # indent each line with 3 spaces for consistency + json=$(sed -e 's/^[ \t]*/ /' -e '1s/^[ \t,]*//' -e '$s/[ \t,]*$//' "$json_config") + printf ' ,%s\n' "$json" + fi + echo "}" +) +echo "$json_configuration" | require="$config_deployment_requires" \ + __file "$conf_dir/$conf_file" \ + --owner root --group "$group" --mode 640 \ + --state "$state" \ + --source - + +# Set configuration deployment as requirement for service restart. +restart_requires="__file/$conf_dir/$conf_file" + +### +# Restart consul agent after everything else. +require="$restart_requires" __service consul --action restart diff --git a/cdist/conf/type/__consul_agent/parameter/boolean b/cdist/conf/type/__consul_agent/parameter/boolean index 91f7f17e..c86853c3 100644 --- a/cdist/conf/type/__consul_agent/parameter/boolean +++ b/cdist/conf/type/__consul_agent/parameter/boolean @@ -6,3 +6,4 @@ server enable-syslog verify-incoming verify-outgoing +use-distribution-package diff --git a/cdist/conf/type/__consul_check/explorer/conf-dir b/cdist/conf/type/__consul_check/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_check/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_check/manifest b/cdist/conf/type/__consul_check/manifest index 8149b130..522aa1a9 100755 --- a/cdist/conf/type/__consul_check/manifest +++ b/cdist/conf/type/__consul_check/manifest @@ -19,7 +19,7 @@ # name="$(cat "$__object/parameter/name" 2>/dev/null || echo "$__object_id")" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="check_${name}.json" state="$(cat "$__object/parameter/state")" @@ -40,7 +40,7 @@ if [ ! -f "$__object/parameter/interval" ]; then fi done fi -if [ -f "$__object/parameter/docker-container-id" -a ! -f "$__object/parameter/script" ]; then +if [ -f "$__object/parameter/docker-container-id" ] && [ ! -f "$__object/parameter/script" ]; then echo "When using --docker-container-id you must also define --script." >&2 exit 1 fi @@ -50,7 +50,8 @@ fi echo "{" printf ' "check": {\n' printf ' "name": "%s"\n' "$name" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in state|name) continue ;; *) diff --git a/cdist/conf/type/__consul_service/explorer/conf-dir b/cdist/conf/type/__consul_service/explorer/conf-dir new file mode 100644 index 00000000..0fc9ef84 --- /dev/null +++ b/cdist/conf/type/__consul_service/explorer/conf-dir @@ -0,0 +1,15 @@ +# Determine the configuration directory used by consul. + +check_dir () { + if [ -d "$1" ]; then + printf '%s' "$1" + exit + fi +} + +check_dir '/etc/consul/conf.d' +check_dir '/etc/consul.d' +check_dir '/etc/consul' + +echo 'Could not determine consul configuration dir. Exiting.' >&2 +exit 1 diff --git a/cdist/conf/type/__consul_service/manifest b/cdist/conf/type/__consul_service/manifest index d7a1b6e3..d16f18e0 100755 --- a/cdist/conf/type/__consul_service/manifest +++ b/cdist/conf/type/__consul_service/manifest @@ -19,20 +19,20 @@ # name="$(cat "$__object/parameter/name" 2>/dev/null || echo "$__object_id")" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="service_${name}.json" state="$(cat "$__object/parameter/state")" # Sanity checks -if [ -f "$__object/parameter/check-script" -a -f "$__object/parameter/check-ttl" ]; then +if [ -f "$__object/parameter/check-script" ] && [ -f "$__object/parameter/check-ttl" ]; then echo "Use either --check-script together with --check-interval OR --check-ttl, but not both" >&2 exit 1 fi -if [ -f "$__object/parameter/check-script" -a ! -f "$__object/parameter/check-interval" ]; then +if [ -f "$__object/parameter/check-script" ] && [ ! -f "$__object/parameter/check-interval" ]; then echo "When using --check-script you must also define --check-interval" >&2 exit 1 fi -if [ -f "$__object/parameter/check-http" -a ! -f "$__object/parameter/check-interval" ]; then +if [ -f "$__object/parameter/check-http" ] && [ ! -f "$__object/parameter/check-interval" ]; then echo "When using --check-http you must also define --check-interval" >&2 exit 1 fi @@ -42,9 +42,10 @@ fi echo "{" printf ' "service": {\n' printf ' "name": "%s"\n' "$name" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in - state|name|check-interval) continue ;; + state|name|check-interval|conf-dir) continue ;; check-script) printf ' ,"check": {\n' printf ' "script": "%s"\n' "$(cat "$__object/parameter/check-script")" @@ -85,7 +86,6 @@ echo " }" # end json file echo "}" ) | \ -require="__directory${conf_dir}" \ __config_file "${conf_dir}/${conf_file}" \ --owner root --group consul --mode 640 \ --state "$state" \ diff --git a/cdist/conf/type/__consul_template/files/consul-template.sysv b/cdist/conf/type/__consul_template/files/consul-template.sysv index 0a463020..b263915a 100644 --- a/cdist/conf/type/__consul_template/files/consul-template.sysv +++ b/cdist/conf/type/__consul_template/files/consul-template.sysv @@ -10,72 +10,75 @@ # pidfile: /var/run/consul-template/pidfile # Source function library. + +# shellcheck disable=SC1091 . /etc/init.d/functions NAME=consul-template CONSUL_TEMPLATE=/usr/local/bin/consul-template -CONFIG=/etc/$NAME/conf.d -PID_FILE=/var/run/$NAME/pidfile -LOG_FILE=/var/log/$NAME +CONFIG="/etc/$NAME/conf.d" +PID_FILE="/var/run/$NAME/pidfile" +LOG_FILE="/var/log/$NAME" -[ -e /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME -export CONSUL_TEMPLATE_LOG=${CONSUL_TEMPLATE_LOG:-info} -export GOMAXPROCS=${GOMAXPROCS:-2} +# shellcheck disable=SC1090 +[ -e "/etc/sysconfig/$NAME" ] && . "/etc/sysconfig/$NAME" +export CONSUL_TEMPLATE_LOG="${CONSUL_TEMPLATE_LOG:-info}" +export GOMAXPROCS="${GOMAXPROCS:-2}" -mkdir -p /var/run/$NAME +mkdir -p "/var/run/$NAME" start() { - echo -n "Starting $NAME: " + printf "Starting %s: " "$NAME" daemon --pidfile="$PID_FILE" \ "$CONSUL_TEMPLATE" -config "$CONFIG" >> "$LOG_FILE" 2>&1 & - echo $! > "$PID_FILE" + echo "$!" > "$PID_FILE" retcode=$? - touch /var/lock/subsys/$NAME - return $retcode + touch "/var/lock/subsys/$NAME" + return "$retcode" } stop() { - echo -n "Shutting down $NAME: " - killproc -p $PID_FILE $CONSUL_TEMPLATE + printf "Shutting down %s: " "$NAME" + killproc -p "$PID_FILE" "$CONSUL_TEMPLATE" retcode=$? - rm -f /var/lock/subsys/$NAME - return $retcode + rm -f "/var/lock/subsys/$NAME" + return "$retcode" } case "$1" in start) - if $(status -p "$PID_FILE" $NAME >/dev/null); then + if status -p "$PID_FILE" "$NAME" >/dev/null; then echo "$NAME already running" else start fi ;; stop) - if $(status -p "$PID_FILE" $NAME >/dev/null); then + if status -p "$PID_FILE" "$NAME" >/dev/null; then stop else echo "$NAME not running" fi ;; status) - status -p "$PID_FILE" $NAME + status -p "$PID_FILE" "$NAME" exit $? ;; restart) - if $(status -p "$PID_FILE" $NAME >/dev/null); then + if status -p "$PID_FILE" "$NAME" >/dev/null; then stop fi start ;; reload) - if $(status -p "$PID_FILE" $NAME >/dev/null); then - kill -HUP `cat $PID_FILE` + if status -p "$PID_FILE" "$NAME" >/dev/null; then + kill -HUP "$(cat "$PID_FILE")" else echo "$NAME not running" fi ;; condrestart) - if [ -f /var/lock/subsys/$NAME ]; then - if $(status -p "$PID_FILE" $NAME >/dev/null); then + if [ -f "/var/lock/subsys/$NAME" ]; then + if status -p "$PID_FILE" "$NAME" >/dev/null; then stop fi start diff --git a/cdist/conf/type/__consul_template/manifest b/cdist/conf/type/__consul_template/manifest index 2236e5bd..b02fc332 100755 --- a/cdist/conf/type/__consul_template/manifest +++ b/cdist/conf/type/__consul_template/manifest @@ -75,7 +75,8 @@ require="__directory/etc/consul-template" \ # Generate hcl config file ( -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in auth-password|state|ssl-*|syslog-*|version|vault-token|vault-ssl*) continue ;; auth-username) diff --git a/cdist/conf/type/__consul_template_template/manifest b/cdist/conf/type/__consul_template_template/manifest index 5fe657d0..1eae1fad 100755 --- a/cdist/conf/type/__consul_template_template/manifest +++ b/cdist/conf/type/__consul_template_template/manifest @@ -26,32 +26,36 @@ template_dir="/etc/consul-template/template" require="" # Sanity checks -if [ -f "$__object/parameter/source" -a -f "$__object/parameter/source-file" ]; then +if [ -f "$__object/parameter/source" ] && [ -f "$__object/parameter/source-file" ]; then echo "Use either --source OR --source-file, but not both." >&2 exit 1 fi -if [ ! -f "$__object/parameter/source" -a ! -f "$__object/parameter/source-file" ]; then +if [ ! -f "$__object/parameter/source" ] && [ ! -f "$__object/parameter/source-file" ]; then echo "Either --source OR --source-file must be given." >&2 exit 1 fi +if [ -f "$__object/parameter/source-file" ]; then + destination="${template_dir}/${name}" + require="__file${destination}" +fi + # Generate hcl config file -( +{ printf 'template {\n' -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in source-file) source="$(cat "$__object/parameter/$param")" if [ "$source" = "-" ]; then source="$__object/stdin" fi - destination="${template_dir}/${name}" require="__directory${template_dir}" \ __file "$destination" \ --owner root --group root --mode 640 \ --source "$source" \ --state "$state" - export require="__file${destination}" printf ' source = "%s"\n' "$destination" ;; @@ -65,7 +69,7 @@ for param in $(ls "$__object/parameter/"); do esac done printf '}\n' -) | \ +} | \ require="$require __directory${conf_dir}" \ __config_file "${conf_dir}/${conf_file}" \ --owner root --group root --mode 640 \ diff --git a/cdist/conf/type/__consul_watch_checks/explorer/conf-dir b/cdist/conf/type/__consul_watch_checks/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_checks/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_checks/manifest b/cdist/conf/type/__consul_watch_checks/manifest index ebb49e2e..4976b25a 100755 --- a/cdist/conf/type/__consul_watch_checks/manifest +++ b/cdist/conf/type/__consul_watch_checks/manifest @@ -20,12 +20,12 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" # Sanity checks -if [ -f "$__object/parameter/filter-service" -a -f "$__object/parameter/filter-state" ]; then +if [ -f "$__object/parameter/filter-service" ] && [ -f "$__object/parameter/filter-state" ]; then echo "Use either --filter-service or --filter-state but not both." >&2 exit 1 fi @@ -35,7 +35,8 @@ fi echo "{" printf ' "watches": [{\n' printf ' "type": "%s"\n' "$watch_type" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in state) continue ;; filter-*) diff --git a/cdist/conf/type/__consul_watch_event/explorer/conf-dir b/cdist/conf/type/__consul_watch_event/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_event/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_event/manifest b/cdist/conf/type/__consul_watch_event/manifest index 099054a5..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_event/manifest +++ b/cdist/conf/type/__consul_watch_event/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" @@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")" echo "{" printf ' "watches": [{\n' printf ' "type": "%s"\n' "$watch_type" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in state) continue ;; *) diff --git a/cdist/conf/type/__consul_watch_key/explorer/conf-dir b/cdist/conf/type/__consul_watch_key/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_key/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_key/manifest b/cdist/conf/type/__consul_watch_key/manifest index 099054a5..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_key/manifest +++ b/cdist/conf/type/__consul_watch_key/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" @@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")" echo "{" printf ' "watches": [{\n' printf ' "type": "%s"\n' "$watch_type" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in state) continue ;; *) diff --git a/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir b/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_keyprefix/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_keyprefix/manifest b/cdist/conf/type/__consul_watch_keyprefix/manifest index 099054a5..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_keyprefix/manifest +++ b/cdist/conf/type/__consul_watch_keyprefix/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" @@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")" echo "{" printf ' "watches": [{\n' printf ' "type": "%s"\n' "$watch_type" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in state) continue ;; *) diff --git a/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir b/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_nodes/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_nodes/manifest b/cdist/conf/type/__consul_watch_nodes/manifest index 099054a5..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_nodes/manifest +++ b/cdist/conf/type/__consul_watch_nodes/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" @@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")" echo "{" printf ' "watches": [{\n' printf ' "type": "%s"\n' "$watch_type" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in state) continue ;; *) diff --git a/cdist/conf/type/__consul_watch_service/explorer/conf-dir b/cdist/conf/type/__consul_watch_service/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_service/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_service/manifest b/cdist/conf/type/__consul_watch_service/manifest index 2825c716..e8d18328 100755 --- a/cdist/conf/type/__consul_watch_service/manifest +++ b/cdist/conf/type/__consul_watch_service/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" @@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")" echo "{" printf ' "watches": [{\n' printf ' "type": "%s"\n' "$watch_type" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in state) continue ;; passingonly) diff --git a/cdist/conf/type/__consul_watch_services/explorer/conf-dir b/cdist/conf/type/__consul_watch_services/explorer/conf-dir new file mode 120000 index 00000000..daa712c3 --- /dev/null +++ b/cdist/conf/type/__consul_watch_services/explorer/conf-dir @@ -0,0 +1 @@ +../../__consul_service/explorer/conf-dir \ No newline at end of file diff --git a/cdist/conf/type/__consul_watch_services/manifest b/cdist/conf/type/__consul_watch_services/manifest index 099054a5..b17680c1 100755 --- a/cdist/conf/type/__consul_watch_services/manifest +++ b/cdist/conf/type/__consul_watch_services/manifest @@ -20,7 +20,7 @@ cdist_type="${__type##*/}" watch_type="${cdist_type##*_}" -conf_dir="/etc/consul/conf.d" +conf_dir=$(cat "$__object/explorer/conf-dir") conf_file="watch_${watch_type}_${__object_id}.json" state="$(cat "$__object/parameter/state")" @@ -29,7 +29,8 @@ state="$(cat "$__object/parameter/state")" echo "{" printf ' "watches": [{\n' printf ' "type": "%s"\n' "$watch_type" -for param in $(ls "$__object/parameter/"); do +cd "$__object/parameter/" +for param in *; do case "$param" in state) continue ;; *) diff --git a/cdist/conf/type/__cron/explorer/entry b/cdist/conf/type/__cron/explorer/entry index 2167e045..801861a3 100644 --- a/cdist/conf/type/__cron/explorer/entry +++ b/cdist/conf/type/__cron/explorer/entry @@ -24,7 +24,7 @@ user="$(cat "$__object/parameter/user")" if [ -f "$__object/parameter/raw_command" ]; then command="$(cat "$__object/parameter/command")" - crontab -u $user -l 2>/dev/null | grep "^$command\$" || true + crontab -u "$user" -l 2>/dev/null | grep "^$command\$" || true else - crontab -u $user -l 2>/dev/null | grep "# $name\$" || true + crontab -u "$user" -l 2>/dev/null | grep "# $name\$" || true fi diff --git a/cdist/conf/type/__cron/gencode-remote b/cdist/conf/type/__cron/gencode-remote index f58896af..9debbc47 100755 --- a/cdist/conf/type/__cron/gencode-remote +++ b/cdist/conf/type/__cron/gencode-remote @@ -31,24 +31,28 @@ if [ -f "$__object/parameter/raw" ]; then elif [ -f "$__object/parameter/raw_command" ]; then entry="$command" else - minute="$(cat "$__object/parameter/minute" 2>/dev/null || echo "*")" - hour="$(cat "$__object/parameter/hour" 2>/dev/null || echo "*")" - day_of_month="$(cat "$__object/parameter/day_of_month" 2>/dev/null || echo "*")" - month="$(cat "$__object/parameter/month" 2>/dev/null || echo "*")" - day_of_week="$(cat "$__object/parameter/day_of_week" 2>/dev/null || echo "*")" + minute="$(cat "$__object/parameter/minute")" + hour="$(cat "$__object/parameter/hour")" + day_of_month="$(cat "$__object/parameter/day_of_month")" + month="$(cat "$__object/parameter/month")" + day_of_week="$(cat "$__object/parameter/day_of_week")" entry="$minute $hour $day_of_month $month $day_of_week $command # $name" fi mkdir "$__object/files" echo "$entry" > "$__object/files/entry" -if diff -q "$__object/files/entry" "$__object/explorer/entry" >/dev/null; then - state_is=present +if [ -s "$__object/explorer/entry" ]; then + if diff -q "$__object/files/entry" "$__object/explorer/entry" >/dev/null; then + state_is=present + else + state_is=modified + fi else state_is=absent fi -state_should="$(cat "$__object/parameter/state" 2>/dev/null || echo "present")" +state_should="$(cat "$__object/parameter/state")" [ "$state_is" = "$state_should" ] && exit 0 @@ -58,7 +62,7 @@ state_should="$(cat "$__object/parameter/state" 2>/dev/null || echo "present")" # These are the old markers prefix="#cdist:__cron/$__object_id" suffix="#/cdist:__cron/$__object_id" -filter="^# DO NOT EDIT THIS FILE|^# \(.* installed on |^# \(Cron version V|^# \(Cronie version .\..\)$" +filter='^# DO NOT EDIT THIS FILE|^# \(.* installed on |^# \(Cron version V|^# \(Cronie version .\..\)$' cat << DONE crontab -u $user -l 2>/dev/null | grep -v -E "$filter" | awk -v prefix="$prefix" -v suffix="$suffix" ' { diff --git a/cdist/conf/type/__cron/man.rst b/cdist/conf/type/__cron/man.rst index d0694738..e39bfb5c 100644 --- a/cdist/conf/type/__cron/man.rst +++ b/cdist/conf/type/__cron/man.rst @@ -21,6 +21,11 @@ command OPTIONAL PARAMETERS ------------------- +**NOTE**: All time-related parameters (``--minute``, ``--hour``, ``--day_of_month`` +``--month`` and ``--day_of_week``) defaults to ``*``, which means to execute it +**always**. If you set ``--hour 0`` to execute the cronjob only at midnight, it +will execute **every** minute in the first hour of the morning all days. + state Either present or absent. Defaults to present. minute diff --git a/cdist/conf/type/__cron/manifest b/cdist/conf/type/__cron/manifest index 53973e07..e7b51863 100755 --- a/cdist/conf/type/__cron/manifest +++ b/cdist/conf/type/__cron/manifest @@ -22,3 +22,12 @@ if [ -f "$__object/parameter/raw" ] && [ -f "$__object/parameter/raw_command" ]; echo "ERROR: both raw and raw_command specified" >&2 exit 1 fi + +case "$(cat "$__object/parameter/state")" in + present) ;; + absent) ;; + + *) + echo "ERROR: unkown cron state" >&2 + exit 2 +esac diff --git a/cdist/conf/type/__cron/nonparallel b/cdist/conf/type/__cron/nonparallel new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__cron/parameter/default/day_of_month b/cdist/conf/type/__cron/parameter/default/day_of_month new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/day_of_month @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/day_of_week b/cdist/conf/type/__cron/parameter/default/day_of_week new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/day_of_week @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/hour b/cdist/conf/type/__cron/parameter/default/hour new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/hour @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/minute b/cdist/conf/type/__cron/parameter/default/minute new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/minute @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/month b/cdist/conf/type/__cron/parameter/default/month new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/month @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__cron/parameter/default/state b/cdist/conf/type/__cron/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__cron/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__daemontools/files/init.d-svscan b/cdist/conf/type/__daemontools/files/init.d-svscan index 127dfdb3..996eb4e8 100644 --- a/cdist/conf/type/__daemontools/files/init.d-svscan +++ b/cdist/conf/type/__daemontools/files/init.d-svscan @@ -23,9 +23,9 @@ fi case "$1" in start) - echo -n "Starting daemontools: " - if [ ! `pidof svscan` ]; then - echo -n "svscan " + printf "Starting daemontools: " + if ! pidof svscan > /dev/null 2>&1; then + printf "svscan " env - PATH="$PATH" svscan /service 2>&1 | setuidgid daemon multilog t /var/log/svscan & echo "." else @@ -33,23 +33,28 @@ case "$1" in fi ;; stop) - echo -n "Stopping daemontools: " - if [ `pidof svscan` ]; then - echo -n "svscan" - while [ `pidof svscan` ]; do - kill `pidof svscan` - echo -n "." + printf "Stopping daemontools: " + pids="$(pidof svscan)" + if [ -n "${pids}" ] + then + printf "svscan" + while [ -n "${pids}" ] + do + # shellcheck disable=SC2086 + kill ${pids} + printf "." + pids="$(pidof svscan)" done fi - echo -n " services" - for i in `ls -d /service/*`; do - svc -dx $i - echo -n "." + printf " services" + for i in /service/*; do + svc -dx "$i" + printf "." done - echo -n " logging " - for i in `ls -d /service/*/log`; do - svc -dx $i - echo -n "." + printf " logging " + for i in /service/*/log; do + svc -dx "$i" + printf "." done echo "" ;; diff --git a/cdist/conf/type/__daemontools/manifest b/cdist/conf/type/__daemontools/manifest index 45ce3df6..b04c7e07 100755 --- a/cdist/conf/type/__daemontools/manifest +++ b/cdist/conf/type/__daemontools/manifest @@ -3,12 +3,13 @@ pkg=$(cat "$__object/parameter/from-package") servicedir=$(cat "$__object/parameter/servicedir") -__package $pkg -__directory $servicedir --mode 700 +__package "$pkg" +__directory "$servicedir" --mode 700 os=$(cat "$__global/explorer/os") init=$(cat "$__global/explorer/init") +require="" case $os in freebsd) # TODO change to __start_on_boot once it supports freebsd diff --git a/cdist/conf/type/__daemontools_service/explorer/svc b/cdist/conf/type/__daemontools_service/explorer/svc old mode 100644 new mode 100755 index d33fcea4..9ba462f2 --- a/cdist/conf/type/__daemontools_service/explorer/svc +++ b/cdist/conf/type/__daemontools_service/explorer/svc @@ -1 +1,2 @@ +#!/bin/sh command -v svc || true diff --git a/cdist/conf/type/__daemontools_service/manifest b/cdist/conf/type/__daemontools_service/manifest index 9e8e0bee..78bae285 100755 --- a/cdist/conf/type/__daemontools_service/manifest +++ b/cdist/conf/type/__daemontools_service/manifest @@ -25,14 +25,14 @@ badusage() { [ -z "$run$runfile" ] && badusage [ -n "$run" ] && [ -n "$runfile" ] && badusage -__directory $servicedir/$name/log/main --parents +__directory "$servicedir/$name/log/main" --parents echo "$RUN_PREFIX$run" | require="__directory/$servicedir/$name/log/main" __config_file "$servicedir/$name/run" \ --onchange "svc -t '$servicedir/$name' 2>/dev/null" \ --mode 755 \ --source "${runfile:--}" -echo "$RUN_PREFIX$logrun" | require="__directory/$servicedir/$name/log/main" __config_file $servicedir/$name/log/run \ +echo "$RUN_PREFIX$logrun" | require="__directory/$servicedir/$name/log/main" __config_file "$servicedir/$name/log/run" \ --onchange "svc -t '$servicedir/$name/log' 2>/dev/null" \ --mode 755 \ --source "-" diff --git a/cdist/conf/type/__directory/explorer/stat b/cdist/conf/type/__directory/explorer/stat index 41bc8b04..f817cb02 100755 --- a/cdist/conf/type/__directory/explorer/stat +++ b/cdist/conf/type/__directory/explorer/stat @@ -1,6 +1,7 @@ #!/bin/sh # # 2013 Steven Armstrong (steven-cdist armstrong.cc) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -20,31 +21,51 @@ destination="/$__object_id" +fallback() { + # Patch the output together, manually + + ls_line=$(ls -ldn "$destination") + + uid=$(echo "$ls_line" | awk '{ print $3 }') + gid=$(echo "$ls_line" | awk '{ print $4 }') + + owner=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/passwd) + group=$(awk -F: -v gid="$gid" '$3 == gid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/group) + + mode_text=$(echo "$ls_line" | awk '{ print $1 }') + mode=$(echo "$mode_text" | awk '{for(i=8;i>=0;--i){c=substr($1,10-i,1);k+=((c~/[rwxst]/)*2^i);if(!(i%3))k+=(tolower(c)~/[lst]/)*2^(9+i/3)}printf("%04o",k)}') + + printf 'type: %s\nowner: %d %s\ngroup: %d %s\nmode: %s %s\n' \ + "$("$__type_explorer/type")" \ + "$uid" "$owner" \ + "$gid" "$group" \ + "$mode" "$mode_text" +} + # nothing to work with, nothing we could do [ -e "$destination" ] || exit 0 -os=$("$__explorer/os") -case "$os" in - "freebsd"|"netbsd"|"openbsd") - # FIXME: should be something like this based on man page, but can not test - stat -f "type: %ST +command -v stat >/dev/null 2>&1 || { + fallback + exit +} + +case $("$__explorer/os") +in + freebsd|netbsd|openbsd|macosx) + stat -f 'type: %HT owner: %Du %Su group: %Dg %Sg -mode: %Op %Sp -" "$destination" - ;; - "macosx") - stat -f "type: %HT - owner: %Du %Su - group: %Dg %Sg - mode: %Lp %Sp - " "$destination" - ;; +mode: %Mp%03Lp %Sp +' "$destination" | awk '/^type/ { print tolower($0); next } { print }' + ;; *) - stat --printf="type: %F + # NOTE: Do not use --printf here as it is not supported by BusyBox stat. + # NOTE: BusyBox's stat might not support the "-c" option, in which case + # we fall through to the shell fallback. + stat -c 'type: %F owner: %u %U group: %g %G -mode: %a %A -" "$destination" - ;; +mode: %04a %A' "$destination" 2>/dev/null || fallback + ;; esac diff --git a/cdist/conf/type/__directory/gencode-remote b/cdist/conf/type/__directory/gencode-remote index cced4624..d9c00b56 100755 --- a/cdist/conf/type/__directory/gencode-remote +++ b/cdist/conf/type/__directory/gencode-remote @@ -3,6 +3,7 @@ # 2011-2013 Nico Schottelius (nico-cdist at schottelius.org) # 2013 Steven Armstrong (steven-cdist armstrong.cc) # 2014 Daniel Heule (hda at sfs.biz) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -21,8 +22,8 @@ # destination="/$__object_id" -state_should="$(cat "$__object/parameter/state")" -type="$(cat "$__object/explorer/type")" +state_should=$(cat "$__object/parameter/state") +type=$(cat "$__object/explorer/type") stat_file="$__object/explorer/stat" # variable to keep track if we have to set directory attributes @@ -57,32 +58,36 @@ get_current_value() { } set_group() { - echo chgrp $recursive \"$1\" \"$destination\" - echo chgrp $recursive $1 >> "$__messages_out" + echo "chgrp $recursive '$1' '$destination'" + echo "chgrp $recursive '$1'" >> "$__messages_out" } set_owner() { - echo chown $recursive \"$1\" \"$destination\" - echo chown $recursive $1 >> "$__messages_out" + echo "chown $recursive '$1' '$destination'" + echo "chown $recursive '$1'" >> "$__messages_out" } set_mode() { - echo chmod $recursive \"$1\" \"$destination\" - echo chmod $recursive $1 >> "$__messages_out" + echo "chmod $recursive '$1' '$destination'" + echo "chmod $recursive '$1'" >> "$__messages_out" } case "$state_should" in - present) + present|exists) if [ "$type" != "directory" ]; then set_attributes=1 if [ "$type" != "none" ]; then # our destination is not a directory, remove whatever is there # and then create our directory and set all attributes - echo rm -f "\"$destination\"" + echo "rm -f '$destination'" echo "remove non directory" >> "$__messages_out" fi - echo "mkdir $mkdiropt \"$destination\"" + echo "mkdir $mkdiropt '$destination'" echo "create" >> "$__messages_out" + elif [ "$state_should" = 'exists' ]; then + # The type is directory and --state exists. We are done and do not + # check or set the attributes. + exit 0 fi # Note: Mode - needs to happen last as a chown/chgrp can alter mode by @@ -92,9 +97,11 @@ case "$state_should" in value_should="$(cat "$__object/parameter/$attribute")" value_is="$(get_current_value "$attribute" "$value_should")" - # change 0xxx format to xxx format => same as stat returns + # format mode in four digits => same as stat returns if [ "$attribute" = mode ]; then - value_should="$(echo $value_should | sed 's/^0\(...\)/\1/')" + # Convert to four-digit octal number (printf interprets + # strings with leading 0s as octal!) + value_should=$(printf '%04o' "0${value_should}") fi if [ "$set_attributes" = 1 ] || [ "$value_should" != "$value_is" ]; then @@ -103,9 +110,29 @@ case "$state_should" in fi done ;; + pre-exists) + case $type in + directory) + # all good + exit 0 + ;; + none) + printf 'Directory "%s" does not exist\n' "$destination" >&2 + exit 1 + ;; + file|symlink) + printf 'File "%s" exists and is a %s, but should be a directory\n' "$destination" "$type" >&2 + exit 1 + ;; + *) + printf 'File or directory "%s" is in an unknown state\n' "$destination" >&2 + exit 1 + ;; + esac + ;; absent) if [ "$type" = "directory" ]; then - echo rm -rf \"$destination\" + echo "rm -rf '$destination'" echo remove >> "$__messages_out" fi ;; diff --git a/cdist/conf/type/__directory/man.rst b/cdist/conf/type/__directory/man.rst index 74b00afe..7755334c 100644 --- a/cdist/conf/type/__directory/man.rst +++ b/cdist/conf/type/__directory/man.rst @@ -19,7 +19,18 @@ None. OPTIONAL PARAMETERS ------------------- state - 'present' or 'absent', defaults to 'present' + 'present', 'absent', 'exists' or 'pre-exists', defaults to 'present' where: + + present + the directory exists and the given attributes are set. + absent + the directory does not exist. + exists + the directory exists, but its attributes are not altered if it already + existed. + pre-exists + check that the directory exists and is indeed a directory, but do not + create or modify it. group Group to chgrp to. @@ -36,7 +47,7 @@ BOOLEAN PARAMETERS parents Whether to create parents as well (mkdir -p behaviour). Warning: all intermediate directory permissions default - to whatever mkdir -p does. + to whatever mkdir -p does. Usually this means root:root, 0700. diff --git a/cdist/conf/type/__docker/man.rst b/cdist/conf/type/__docker/man.rst index 70b92cc7..718543a8 100644 --- a/cdist/conf/type/__docker/man.rst +++ b/cdist/conf/type/__docker/man.rst @@ -3,12 +3,12 @@ cdist-type__docker(7) NAME ---- -cdist-type__docker - install docker-engine +cdist-type__docker - install Docker CE DESCRIPTION ----------- -Installs latest docker-engine package from dockerproject.org. +Installs latest Docker Community Edition package. REQUIRED PARAMETERS @@ -18,16 +18,16 @@ None. OPTIONAL PARAMETERS ------------------- -None. +state + 'present' or 'absent', defaults to 'present' +version + The specific version to install. Defaults to the special value 'latest', + meaning the version the package manager will install by default. BOOLEAN PARAMETERS ------------------ -experimental - Install the experimental docker-engine package instead of the latest stable release. - -state - 'present' or 'absent', defaults to 'present' +None. EXAMPLES @@ -38,12 +38,11 @@ EXAMPLES # Install docker __docker - # Install experimental - __docker --experimental - # Remove docker __docker --state absent + # Install specific version + __docker --state present --version 18.03.0.ce AUTHORS ------- diff --git a/cdist/conf/type/__docker/manifest b/cdist/conf/type/__docker/manifest index 1b1b1fb7..6a57d85a 100755 --- a/cdist/conf/type/__docker/manifest +++ b/cdist/conf/type/__docker/manifest @@ -21,58 +21,84 @@ os=$(cat "$__global/explorer/os") state=$(cat "$__object/parameter/state") +version=$(cat "$__object/parameter/version") case "$os" in centos) - component="main" - if [ -f "$__object/parameter/experimental" ]; then - component="experimental" + # shellcheck source=/dev/null + if (. "$__global/explorer/os_release" && [ "${VERSION_ID}" = "7" ]); then + __yum_repo docker-ce-stable \ + --name 'Docker CE Stable' \ + --baseurl "https://download.docker.com/linux/centos/7/\$basearch/stable" \ + --enabled \ + --gpgcheck 1 \ + --gpgkey 'https://download.docker.com/linux/centos/gpg' \ + --state "${state}" + if [ "$version" != "latest" ]; then + require="__yum_repo/docker-ce-stable" __package docker-ce --version "${version}" --state "${state}" + else + require="__yum_repo/docker-ce-stable" __package docker-ce --state "${state}" + fi + else + echo "CentOS version 7 is required!" >&2 + exit 1 fi - __yum_repo docker \ - --name 'Docker Repository' \ - --baseurl "https://yum.dockerproject.org/repo/$component/centos/\$releasever/" \ - --enabled \ - --gpgcheck 1 \ - --gpgkey 'https://yum.dockerproject.org/gpg' \ - --state ${state} - require="__yum_repo/docker" __package docker-engine --state ${state} ;; - ubuntu) - component="main" - if [ -f "$__object/parameter/experimental" ]; then - component="experimental" - fi - __package apparmor --state ${state} - __package ca-certificates --state ${state} - __package apt-transport-https --state ${state} - __apt_key docker --keyid 58118E89F3A912897C070ADBF76221572C52609D --state ${state} - export CDIST_ORDER_DEPENDENCY=on - __apt_source docker \ - --uri https://apt.dockerproject.org/repo \ - --distribution "ubuntu-$(cat "$__global/explorer/lsb_codename")" \ - --state ${state} \ - --component "$component" - __package docker-engine --state ${state} - unset CDIST_ORDER_DEPENDENCY - ;; - debian) - component="main" - if [ -f "$__object/parameter/experimental" ]; then - component="experimental" + ubuntu|debian) + if [ "${state}" = "present" ]; then + __package apt-transport-https + __package ca-certificates + __package gnupg2 fi + __apt_key_uri docker --name "Docker Release (CE deb) " \ + --uri "https://download.docker.com/linux/${os}/gpg" --state "${state}" - __package apt-transport-https --state ${state} - __package ca-certificates --state ${state} - __package gnupg2 --state ${state} - __apt_key docker --keyid 58118E89F3A912897C070ADBF76221572C52609D --state ${state} - export CDIST_ORDER_DEPENDENCY=on - __apt_source docker \ - --uri https://apt.dockerproject.org/repo \ - --distribution "debian-$(cat "$__global/explorer/lsb_codename")" \ - --state ${state} \ - --component "$component" - __package docker-engine --state ${state} - unset CDIST_ORDER_DEPENDENCY + require="__apt_key_uri/docker" __apt_source docker \ + --uri "https://download.docker.com/linux/${os}" \ + --distribution "$(cat "$__global/explorer/lsb_codename")" \ + --state "${state}" \ + --component "stable" + if [ "$version" != "latest" ]; then + require="__apt_source/docker" __package docker-ce --version "${version}" --state "${state}" + else + require="__apt_source/docker" __package docker-ce --state "${state}" + fi + ;; + devuan) + os_version="$(cat "$__global/explorer/os_version")" + + case "$os_version" in + ascii) + distribution="stretch" + ;; + jessie) + distribution="jessie" + ;; + *) + echo "Your devuan release ($os_version) is currently not supported by this type (${__type##*/}).">&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 + ;; + esac + + if [ "${state}" = "present" ]; then + __package apt-transport-https + __package ca-certificates + __package gnupg2 + fi + __apt_key_uri docker --name "Docker Release (CE deb) " \ + --uri "https://download.docker.com/linux/${os}/gpg" --state "${state}" + + require="__apt_key_uri/docker" __apt_source docker \ + --uri "https://download.docker.com/linux/${os}" \ + --distribution "${distribution}" \ + --state "${state}" \ + --component "stable" + if [ "$version" != "latest" ]; then + require="__apt_source/docker" __package docker-ce --version "${version}" --state "${state}" + else + require="__apt_source/docker" __package docker-ce --state "${state}" + fi ;; *) diff --git a/cdist/conf/type/__docker/parameter/boolean b/cdist/conf/type/__docker/parameter/boolean deleted file mode 100644 index 9839eb20..00000000 --- a/cdist/conf/type/__docker/parameter/boolean +++ /dev/null @@ -1 +0,0 @@ -experimental diff --git a/cdist/conf/type/__docker/parameter/default/version b/cdist/conf/type/__docker/parameter/default/version new file mode 100644 index 00000000..a0f9a4b4 --- /dev/null +++ b/cdist/conf/type/__docker/parameter/default/version @@ -0,0 +1 @@ +latest diff --git a/cdist/conf/type/__docker/parameter/optional b/cdist/conf/type/__docker/parameter/optional index ff72b5c7..4d595ed7 100644 --- a/cdist/conf/type/__docker/parameter/optional +++ b/cdist/conf/type/__docker/parameter/optional @@ -1 +1,2 @@ state +version diff --git a/cdist/conf/type/__docker_compose/gencode-remote b/cdist/conf/type/__docker_compose/gencode-remote index 2b8267a9..77fc2fdf 100755 --- a/cdist/conf/type/__docker_compose/gencode-remote +++ b/cdist/conf/type/__docker_compose/gencode-remote @@ -22,10 +22,11 @@ version="$(cat "$__object/parameter/version")" state="$(cat "$__object/parameter/state")" -if [ ${state} = "present" ]; then +if [ "${state}" = "present" ]; then # Download docker-compose file - echo 'curl -L "https://github.com/docker/compose/releases/download/'${version}'/docker-compose-$(uname -s)-$(uname -m)" -o /tmp/docker-compose' - echo 'mv /tmp/docker-compose /usr/local/bin/docker-compose' + #shellcheck disable=SC2016 + echo 'curl -L "https://github.com/docker/compose/releases/download/'"${version}"'/docker-compose-$(uname -s)-$(uname -m)" -o /tmp/docker-compose' + echo 'mv /tmp/docker-compose /usr/local/bin/docker-compose' # Change permissions echo 'chmod +x /usr/local/bin/docker-compose' fi diff --git a/cdist/conf/type/__docker_compose/manifest b/cdist/conf/type/__docker_compose/manifest index c17f0f33..f7de3a76 100755 --- a/cdist/conf/type/__docker_compose/manifest +++ b/cdist/conf/type/__docker_compose/manifest @@ -22,10 +22,10 @@ state="$(cat "$__object/parameter/state")" # Needed packages -if [ ${state} = "present" ]; then +if [ "${state}" = "present" ]; then __docker __package curl -elif [ ${state} = "absent" ]; then +elif [ "${state}" = "absent" ]; then __file /usr/local/bin/docker-compose --state absent else echo "Unknown state: ${state}" >&2 diff --git a/cdist/conf/type/__install_reboot/manifest b/cdist/conf/type/__docker_config/explorer/config-data similarity index 78% rename from cdist/conf/type/__install_reboot/manifest rename to cdist/conf/type/__docker_config/explorer/config-data index 02689d82..b4bb0e11 100755 --- a/cdist/conf/type/__install_reboot/manifest +++ b/cdist/conf/type/__docker_config/explorer/config-data @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2011 Steven Armstrong (steven-cdist at armstrong.cc) +# 2018 Ľubomír Kučera # # This file is part of cdist. # @@ -18,6 +18,5 @@ # along with cdist. If not, see . # -# set defaults -options="$(cat "$__object/parameter/options" 2>/dev/null \ - || echo "" | tee "$__object/parameter/options")" +docker config inspect "${__object_id:?}" --format '{{json .Spec.Data}}' \ + 2>/dev/null | tr -d '"' | base64 -d diff --git a/cdist/conf/type/__install_umount/manifest b/cdist/conf/type/__docker_config/explorer/config-exists similarity index 78% rename from cdist/conf/type/__install_umount/manifest rename to cdist/conf/type/__docker_config/explorer/config-exists index 42cd19bf..58c207d4 100755 --- a/cdist/conf/type/__install_umount/manifest +++ b/cdist/conf/type/__docker_config/explorer/config-exists @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2011 Steven Armstrong (steven-cdist at armstrong.cc) +# 2018 Ľubomír Kučera # # This file is part of cdist. # @@ -18,6 +18,8 @@ # along with cdist. If not, see . # -# set defaults -target="$(cat "$__object/parameter/target" 2>/dev/null \ - || echo "/target" | tee "$__object/parameter/target")" +if docker config ls | grep -q " ${__object_id:?} "; then + echo yes +else + echo no +fi diff --git a/cdist/conf/type/__docker_config/gencode-remote b/cdist/conf/type/__docker_config/gencode-remote new file mode 100755 index 00000000..65497b7e --- /dev/null +++ b/cdist/conf/type/__docker_config/gencode-remote @@ -0,0 +1,69 @@ +#!/bin/sh -e +# +# 2018 Ľubomír Kučera +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +config="${__object_id:?}" +config_exists=$(cat "${__object:?}/explorer/config-exists") +state=$(cat "${__object:?}/parameter/state") + +case "${state}" in + absent) + if [ "${config_exists}" != "yes" ]; then + exit 0 + fi + + echo "docker config rm \"${config}\"" + ;; + present) + source=$(cat "${__object}/parameter/source") + + if [ -z "${source}" ]; then + exit 0 + fi + + if [ "${source}" = "-" ]; then + source="${__object}/stdin" + fi + + if [ "${config_exists}" = "yes" ]; then + if cmp -s "${source}" "${__object}/explorer/config-data"; then + exit 0 + else + echo "docker config rm \"${config}\"" + fi + fi + + cat <<-EOF + source_file="\$(mktemp cdist.XXXXXXXXXX)" + + base64 -d > "\${source_file}" << eof + $(base64 "${source}") + eof + + docker config create "${config}" "\${source_file}" + + rm "\${source_file}" + EOF + ;; + *) + echo "Unsupported state: ${state}" >&2 + + exit 1 + ;; +esac diff --git a/cdist/conf/type/__docker_config/man.rst b/cdist/conf/type/__docker_config/man.rst new file mode 100644 index 00000000..7c74c8af --- /dev/null +++ b/cdist/conf/type/__docker_config/man.rst @@ -0,0 +1,55 @@ +cdist-type__docker_config(7) +============================ + +NAME +---- + +cdist-type__docker_config - Manage Docker configs + +DESCRIPTION +----------- + +This type manages Docker configs. + +OPTIONAL PARAMETERS +------------------- + +source + Path to the source file. If it is '-' (dash), read standard input. + +state + 'present' or 'absent', defaults to 'present' where: + + present + if the config does not exist, it is created + absent + the config is removed + +CAVEATS +------- + +Since Docker configs cannot be updated once created, this type tries removing +and recreating the config if it changes. If the config is used by a service at +the time of removing, then this type will fail. + +EXAMPLES +-------- + +.. code-block:: sh + + # Creates "foo" config from "bar" source file + __docker_config foo --source bar + + +AUTHORS +------- + +Ľubomír Kučera + +COPYING +------- + +Copyright \(C) 2018 Ľubomír Kučera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__docker_config/parameter/default/source b/cdist/conf/type/__docker_config/parameter/default/source new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__docker_config/parameter/default/state b/cdist/conf/type/__docker_config/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__docker_config/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__docker_config/parameter/optional b/cdist/conf/type/__docker_config/parameter/optional new file mode 100644 index 00000000..d77f3048 --- /dev/null +++ b/cdist/conf/type/__docker_config/parameter/optional @@ -0,0 +1,2 @@ +source +state diff --git a/cdist/conf/type/__docker_secret/explorer/secret-exists b/cdist/conf/type/__docker_secret/explorer/secret-exists new file mode 100755 index 00000000..1405f8bc --- /dev/null +++ b/cdist/conf/type/__docker_secret/explorer/secret-exists @@ -0,0 +1,25 @@ +#!/bin/sh -e +# +# 2018 Ľubomír Kučera +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +if docker secret ls | grep -q " ${__object_id:?} "; then + echo yes +else + echo no +fi diff --git a/cdist/conf/type/__docker_secret/gencode-remote b/cdist/conf/type/__docker_secret/gencode-remote new file mode 100755 index 00000000..c75e91d9 --- /dev/null +++ b/cdist/conf/type/__docker_secret/gencode-remote @@ -0,0 +1,65 @@ +#!/bin/sh -e +# +# 2018 Ľubomír Kučera +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +secret="${__object_id:?}" +secret_exists=$(cat "${__object:?}/explorer/secret-exists") +state=$(cat "${__object:?}/parameter/state") + +case "${state}" in + absent) + if [ "${secret_exists}" != "yes" ]; then + exit 0 + fi + + echo "docker secret rm ${secret}" + ;; + present) + if [ "${secret_exists}" = "yes" ]; then + exit 0 + fi + + source=$(cat "${__object}/parameter/source") + + if [ -z "${source}" ]; then + exit 0 + fi + + if [ "${source}" = "-" ]; then + source="${__object}/stdin" + fi + + cat <<-EOF + source_file="\$(mktemp cdist.XXXXXXXXXX)" + + base64 -d > "\${source_file}" << eof + $(base64 "${source}") + eof + + docker secret create "${secret}" "\${source_file}" + + rm "\${source_file}" + EOF + ;; + *) + echo "Unsupported state: ${state}" >&2 + + exit 1 + ;; +esac diff --git a/cdist/conf/type/__docker_secret/man.rst b/cdist/conf/type/__docker_secret/man.rst new file mode 100644 index 00000000..7fe69623 --- /dev/null +++ b/cdist/conf/type/__docker_secret/man.rst @@ -0,0 +1,54 @@ +cdist-type__docker_secret(7) +============================ + +NAME +---- + +cdist-type__docker_secret - Manage Docker secrets + +DESCRIPTION +----------- + +This type manages Docker secrets. + +OPTIONAL PARAMETERS +------------------- + +source + Path to the source file. If it is '-' (dash), read standard input. + +state + 'present' or 'absent', defaults to 'present' where: + + present + if the secret does not exist, it is created + absent + the secret is removed + +CAVEATS +------- + +Since Docker secrets cannot be updated once created, this type takes no action +if the specified secret already exists. + +EXAMPLES +-------- + +.. code-block:: sh + + # Creates "foo" secret from "bar" source file + __docker_secret foo --source bar + + +AUTHORS +------- + +Ľubomír Kučera + +COPYING +------- + +Copyright \(C) 2018 Ľubomír Kučera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__docker_secret/parameter/default/source b/cdist/conf/type/__docker_secret/parameter/default/source new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__docker_secret/parameter/default/state b/cdist/conf/type/__docker_secret/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__docker_secret/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__docker_secret/parameter/optional b/cdist/conf/type/__docker_secret/parameter/optional new file mode 100644 index 00000000..d77f3048 --- /dev/null +++ b/cdist/conf/type/__docker_secret/parameter/optional @@ -0,0 +1,2 @@ +source +state diff --git a/cdist/conf/type/__docker_stack/gencode-remote b/cdist/conf/type/__docker_stack/gencode-remote index 0d47dc52..586271d0 100755 --- a/cdist/conf/type/__docker_stack/gencode-remote +++ b/cdist/conf/type/__docker_stack/gencode-remote @@ -50,7 +50,7 @@ case "${state}" in eof docker stack deploy --compose-file "\${compose_file}" \ - --prune ${stack} + --prune --with-registry-auth ${stack} rm "\${compose_file}" EOF diff --git a/cdist/conf/type/__docker_swarm/explorer/swarm-state b/cdist/conf/type/__docker_swarm/explorer/swarm-state index 9c1bc32d..2c9fd598 100755 --- a/cdist/conf/type/__docker_swarm/explorer/swarm-state +++ b/cdist/conf/type/__docker_swarm/explorer/swarm-state @@ -18,4 +18,4 @@ # along with cdist. If not, see . # -docker info 2>/dev/null | grep "^Swarm: " | cut -d " " -f 2- +docker info 2>/dev/null | grep '^ *Swarm: ' | awk '{print $2}' diff --git a/cdist/conf/type/__dot_file/explorer/home b/cdist/conf/type/__dot_file/explorer/home index 132cfc71..08d941bf 100755 --- a/cdist/conf/type/__dot_file/explorer/home +++ b/cdist/conf/type/__dot_file/explorer/home @@ -19,7 +19,7 @@ set -eu user="$(cat "${__object}/parameter/user")" -if which getent >/dev/null 2>&1; then +if command -v getent >/dev/null 2>&1; then line=$(getent passwd "${user}") else line=$(grep "^${user}:" /etc/passwd) diff --git a/cdist/conf/type/__dot_file/man.rst b/cdist/conf/type/__dot_file/man.rst index ae65eb95..ba7621a1 100644 --- a/cdist/conf/type/__dot_file/man.rst +++ b/cdist/conf/type/__dot_file/man.rst @@ -25,6 +25,9 @@ user OPTIONAL PARAMETERS ------------------- +dirmode + forwarded to :strong:`__directory` type as mode + mode forwarded to :strong:`__file` type diff --git a/cdist/conf/type/__dot_file/manifest b/cdist/conf/type/__dot_file/manifest index 5e4957e5..02dadf05 100755 --- a/cdist/conf/type/__dot_file/manifest +++ b/cdist/conf/type/__dot_file/manifest @@ -19,6 +19,7 @@ set -eu user="$(cat "${__object}/parameter/user")" home="$(cat "${__object}/explorer/home")" primary_group="$(cat "${__object}/explorer/primary_group")" +dirmode="$(cat "${__object}/parameter/dirmode")" # Create parent directory. Type __directory has flag 'parents', but it # will leave us with root-owned directory in user home, which is not @@ -36,6 +37,7 @@ export CDIST_ORDER_DEPENDENCY for dir ; do __directory "${home}/${dir}" \ --group "${primary_group}" \ + --mode "${dirmode}" \ --owner "${user}" done diff --git a/cdist/conf/type/__dot_file/parameter/default/dirmode b/cdist/conf/type/__dot_file/parameter/default/dirmode new file mode 100644 index 00000000..e9745d1f --- /dev/null +++ b/cdist/conf/type/__dot_file/parameter/default/dirmode @@ -0,0 +1 @@ +0700 diff --git a/cdist/conf/type/__dot_file/parameter/optional b/cdist/conf/type/__dot_file/parameter/optional index ccab9fa6..9f7f83fb 100644 --- a/cdist/conf/type/__dot_file/parameter/optional +++ b/cdist/conf/type/__dot_file/parameter/optional @@ -1,3 +1,4 @@ state mode source +dirmode diff --git a/cdist/conf/type/__download/explorer/remote_cmd b/cdist/conf/type/__download/explorer/remote_cmd new file mode 100755 index 00000000..e3e35b45 --- /dev/null +++ b/cdist/conf/type/__download/explorer/remote_cmd @@ -0,0 +1,19 @@ +#!/bin/sh -e + +if [ -f "$__object/parameter/cmd-get" ] +then + cmd="$( cat "$__object/parameter/cmd-get" )" + +elif command -v curl > /dev/null +then + cmd="curl -L -o - '%s'" + +elif command -v fetch > /dev/null +then + cmd="fetch -o - '%s'" + +else + cmd="wget -O - '%s'" +fi + +echo "$cmd" diff --git a/cdist/conf/type/__download/explorer/state b/cdist/conf/type/__download/explorer/state new file mode 100755 index 00000000..00362545 --- /dev/null +++ b/cdist/conf/type/__download/explorer/state @@ -0,0 +1,72 @@ +#!/bin/sh -e + +dst="/$__object_id" + +if [ ! -f "$dst" ] +then + echo 'absent' + exit 0 +fi + +sum_should="$( cat "$__object/parameter/sum" )" + +if [ -f "$__object/parameter/cmd-sum" ] +then + # shellcheck disable=SC2059 + sum_is="$( eval "$( printf \ + "$( cat "$__object/parameter/cmd-sum" )" \ + "$dst" )" )" +else + os="$( "$__explorer/os" )" + + if echo "$sum_should" | grep -Eq '^[0-9]+\s[0-9]+$' + then + sum_is="$( cksum "$dst" | awk '{print $1" "$2}' )" + + elif echo "$sum_should" | grep -Eiq '^md5:[a-f0-9]{32}$' + then + case "$os" in + freebsd) + sum_is="md5:$( md5 -q "$dst" )" + ;; + *) + sum_is="md5:$( md5sum "$dst" | awk '{print $1}' )" + ;; + esac + + elif echo "$sum_should" | grep -Eiq '^sha1:[a-f0-9]{40}$' + then + case "$os" in + freebsd) + sum_is="sha1:$( sha1 -q "$dst" )" + ;; + *) + sum_is="sha1:$( sha1sum "$dst" | awk '{print $1}' )" + ;; + esac + + elif echo "$sum_should" | grep -Eiq '^sha256:[a-f0-9]{64}$' + then + case "$os" in + freebsd) + sum_is="sha256:$( sha256 -q "$dst" )" + ;; + *) + sum_is="sha256:$( sha256sum "$dst" | awk '{print $1}' )" + ;; + esac + fi +fi + +if [ -z "$sum_is" ] +then + echo 'no checksum from target' >&2 + exit 1 +fi + +if [ "$sum_is" = "$sum_should" ] +then + echo 'present' +else + echo 'mismatch' +fi diff --git a/cdist/conf/type/__download/gencode-local b/cdist/conf/type/__download/gencode-local new file mode 100755 index 00000000..571d2c3c --- /dev/null +++ b/cdist/conf/type/__download/gencode-local @@ -0,0 +1,58 @@ +#!/bin/sh -e + +download="$( cat "$__object/parameter/download" )" + +state_is="$( cat "$__object/explorer/state" )" + +if [ "$download" != 'local' ] || [ "$state_is" = 'present' ] +then + exit 0 +fi + +url="$( cat "$__object/parameter/url" )" + +tmp="$( mktemp )" + +dst="/$__object_id" + +if [ -f "$__object/parameter/cmd-get" ] +then + cmd="$( cat "$__object/parameter/cmd-get" )" + +elif command -v wget > /dev/null +then + cmd="wget -O - '%s'" + +elif command -v curl > /dev/null +then + cmd="curl -L -o - '%s'" + +elif command -v fetch > /dev/null +then + cmd="fetch -o - '%s'" + +else + echo 'no usable locally installed utility for downloading' >&2 + exit 1 +fi + +printf "$cmd > %s\n" \ + "$url" \ + "$tmp" + +if echo "$__target_host" | grep -Eq '^[0-9a-fA-F:]+$' +then + target_host="[$__target_host]" +else + target_host="$__target_host" +fi + +printf '%s %s %s:%s\n' \ + "$__remote_copy" \ + "$tmp" \ + "$target_host" \ + "$dst" + +echo "rm -f '$tmp'" + +echo 'downloaded' > "$__messages_out" diff --git a/cdist/conf/type/__download/gencode-remote b/cdist/conf/type/__download/gencode-remote new file mode 100755 index 00000000..029a0801 --- /dev/null +++ b/cdist/conf/type/__download/gencode-remote @@ -0,0 +1,25 @@ +#!/bin/sh -e + +download="$( cat "$__object/parameter/download" )" + +state_is="$( cat "$__object/explorer/state" )" + +if [ "$download" = 'remote' ] && [ "$state_is" != 'present' ] +then + cmd="$( cat "$__object/explorer/remote_cmd" )" + + url="$( cat "$__object/parameter/url" )" + + dst="/$__object_id" + + printf "$cmd > %s\n" \ + "$url" \ + "$dst" + + echo 'downloaded' > "$__messages_out" +fi + +if [ -f "$__object/parameter/onchange" ] && [ "$state_is" != "present" ] +then + cat "$__object/parameter/onchange" +fi diff --git a/cdist/conf/type/__download/man.rst b/cdist/conf/type/__download/man.rst new file mode 100644 index 00000000..54503470 --- /dev/null +++ b/cdist/conf/type/__download/man.rst @@ -0,0 +1,87 @@ +cdist-type__download(7) +======================= + +NAME +---- +cdist-type__download - Download a file + + +DESCRIPTION +----------- +Destination (``$__object_id``) in target host must be persistent storage +in order to calculate checksum and decide if file must be (re-)downloaded. + +By default type will try to use ``wget``, ``curl`` or ``fetch``. +If download happens in target (see ``--download``) then type will +fallback to (and install) ``wget``. + +If download happens in local machine, then environment variables like +``{http,https,ftp}_proxy`` etc can be used on cdist execution +(``http_proxy=foo cdist config ...``). + + +REQUIRED PARAMETERS +------------------- +url + File's URL. + +sum + Checksum of file going to be downloaded. + By default output of ``cksum`` without filename is expected. + Other hash formats supported with prefixes: ``md5:``, ``sha1:`` and ``sha256:``. + + +OPTIONAL PARAMETERS +------------------- +download + If ``local`` (default), then download file to local storage and copy + it to target host. If ``remote``, then download happens in target. + +cmd-get + Command used for downloading. + Command must output to ``stdout``. + Parameter will be used for ``printf`` and must include only one + format specification ``%s`` which will become URL. + For example: ``wget -O - '%s'``. + +cmd-sum + Command used for checksum calculation. + Command output and ``--sum`` parameter must match. + Parameter will be used for ``printf`` and must include only one + format specification ``%s`` which will become destination. + For example: ``md5sum '%s' | awk '{print $1}'``. + +onchange + Execute this command after download. + + +EXAMPLES +-------- + +.. code-block:: sh + + __directory /opt/cpma + + require='__directory/opt/cpma' \ + __download /opt/cpma/cnq3.zip \ + --url https://cdn.playmorepromode.com/files/cnq3/cnq3-1.51.zip \ + --sum md5:46da3021ca9eace277115ec9106c5b46 + + require='__download/opt/cpma/cnq3.zip' \ + __unpack /opt/cpma/cnq3.zip \ + --backup-destination \ + --preserve-archive \ + --destination /opt/cpma/server + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2020 Ander Punnar. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__download/manifest b/cdist/conf/type/__download/manifest new file mode 100755 index 00000000..7ec8d86d --- /dev/null +++ b/cdist/conf/type/__download/manifest @@ -0,0 +1,6 @@ +#!/bin/sh -e + +if grep -Eq '^wget' "$__object/explorer/remote_cmd" +then + __package wget +fi diff --git a/cdist/conf/type/__download/parameter/default/download b/cdist/conf/type/__download/parameter/default/download new file mode 100644 index 00000000..40830374 --- /dev/null +++ b/cdist/conf/type/__download/parameter/default/download @@ -0,0 +1 @@ +local diff --git a/cdist/conf/type/__download/parameter/optional b/cdist/conf/type/__download/parameter/optional new file mode 100644 index 00000000..838e2fbf --- /dev/null +++ b/cdist/conf/type/__download/parameter/optional @@ -0,0 +1,4 @@ +cmd-get +cmd-sum +download +onchange diff --git a/cdist/conf/type/__download/parameter/required b/cdist/conf/type/__download/parameter/required new file mode 100644 index 00000000..6ea4c38f --- /dev/null +++ b/cdist/conf/type/__download/parameter/required @@ -0,0 +1,2 @@ +url +sum diff --git a/cdist/conf/type/__dpkg_architecture/explorer/architecture b/cdist/conf/type/__dpkg_architecture/explorer/architecture new file mode 100755 index 00000000..03e7e386 --- /dev/null +++ b/cdist/conf/type/__dpkg_architecture/explorer/architecture @@ -0,0 +1,26 @@ +#!/bin/sh -e +# __dpkg_architecture/explorer/architecture +# +# 2020 Matthias Stecher +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +# Get the main architecture of this machine + + +# print or die in the gencode-remote +dpkg --print-architecture || true diff --git a/cdist/conf/type/__dpkg_architecture/explorer/foreign-architectures b/cdist/conf/type/__dpkg_architecture/explorer/foreign-architectures new file mode 100755 index 00000000..a150d307 --- /dev/null +++ b/cdist/conf/type/__dpkg_architecture/explorer/foreign-architectures @@ -0,0 +1,26 @@ +#!/bin/sh -e +# __dpkg_architecture/explorer/foreign-architectures +# +# 2020 Matthias Stecher +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +# Print all additional architectures + + +# print or die in the gencode-remote +dpkg --print-foreign-architectures || true diff --git a/cdist/conf/type/__dpkg_architecture/gencode-remote b/cdist/conf/type/__dpkg_architecture/gencode-remote new file mode 100755 index 00000000..47fb24e7 --- /dev/null +++ b/cdist/conf/type/__dpkg_architecture/gencode-remote @@ -0,0 +1,82 @@ +#!/bin/sh -e +# __dpkg_architecture/gencode-remote +# +# 2020 Matthias Stecher +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + + +# Get parameter and explorer +state_should="$(cat "$__object/parameter/state")" +arch_wanted="$__object_id" +main_arch="$(cat "$__object/explorer/architecture")" + +# Exit here if dpkg do not work (empty explorer) +if [ -z "$main_arch" ]; then + echo "dpkg is not available or unable to detect a architecture!" >&2 + exit 1 +fi + + +# Check if requested architecture is the main one +if [ "$arch_wanted" = "$main_arch" ]; then + # higher than present; we can not remove it + state_is="present" + caution="yes" + +# Check if the architecture not already used +elif grep -qFx "$arch_wanted" "$__object/explorer/foreign-architectures"; then + state_is="present" + +# arch does not exist +else + state_is="absent" +fi + + +# Check what to do +if [ "$state_is" != "$state_should" ]; then + case "$state_should" in + present) + # print add code + printf "dpkg --add-architecture '%s'\n" "$arch_wanted" + # updating the index to make the new architecture available + echo "apt update" + + echo added >> "$__messages_out" + ;; + + absent) + if [ "$caution" ]; then + printf "can not remove the main arch '%s' of the system!\n" "$main_arch" >&2 + exit 1 + fi + + # removing all existing packages for the architecture + printf "apt purge '.*:%s'\n" "$arch_wanted" + # print remove code + printf "dpkg --remove-architecture '%s'\n" "$arch_wanted" + + echo removed >> "$__messages_out" + ;; + + *) + printf "state '%s' is unknown!\n" "$state_should" >&2 + exit 1 + ;; + esac +fi diff --git a/cdist/conf/type/__dpkg_architecture/man.rst b/cdist/conf/type/__dpkg_architecture/man.rst new file mode 100644 index 00000000..fa196229 --- /dev/null +++ b/cdist/conf/type/__dpkg_architecture/man.rst @@ -0,0 +1,103 @@ +cdist-type__dpkg_architecture(7) +================================ + +NAME +---- +cdist-type__dpkg_architecture - Handles foreign architectures on debian-like +systems managed by `dpkg` + + +DESCRIPTION +----------- +This type handles foreign architectures on systems managed by +:strong:`dpkg`\ (1). The object id is the name of the architecture accepted by +`dpkg`, which should be added or removed. + +If the architecture is not setup on the system, it adds a new architecture as a +new foreign architecture in `dpkg`. Then, it updates the apt package index to +make packages from the new architecture available. + +If the architecture should be removed, it will remove it if it is not the base +architecture on where the system was installed on. Before it, it will purge +every package based on the "to be removed" architecture via `apt` to be able to +remove the selected architecture. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +state + ``present`` or ``absent``. Defaults to ``present``. + + +MESSAGES +-------- +added + Added the specified architecture + +removed + Removed the specified architecture + + +ABORTS +------ +Aborts in the following cases: + +If :strong:`dpkg`\ (1) is not available. It will abort with a proper error +message. + +If the architecture is the same as the base architecture the system is build +upon it (returned by ``dpkg --print-architecture``) and it should be removed. + +It will fail if it can not execute :strong:`apt`\ (8). It is assumed that it is +already installed. + + +EXAMPLES +-------- + +.. code-block:: sh + + # add i386 (32 bit) architecture + __dpkg_architecture i386 + + # remove it again :) + __dpkg_architecture i386 --state absent + + +SEE ALSO +-------- +`Multiarch on Debian systems `_ + +`How to setup multiarch on Debian `_ + +:strong:`dpkg`\ (1) +:strong:`cdist-type__package_dpkg`\ (7) +:strong:`cdist-type__package_apt`\ (7) + +Useful commands: + +.. code-block:: sh + + # base architecture installed on this system + dpkg --print-architecture + + # extra architectures added + dpkg --print-foreign-architectures + + +AUTHORS +------- +Matthias Stecher + + +COPYING +------- +Copyright \(C) 2020 Matthias Stecher. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +ublished by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__dpkg_architecture/nonparallel b/cdist/conf/type/__dpkg_architecture/nonparallel new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__dpkg_architecture/parameter/default/state b/cdist/conf/type/__dpkg_architecture/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__dpkg_architecture/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__dpkg_architecture/parameter/optional b/cdist/conf/type/__dpkg_architecture/parameter/optional new file mode 100644 index 00000000..ff72b5c7 --- /dev/null +++ b/cdist/conf/type/__dpkg_architecture/parameter/optional @@ -0,0 +1 @@ +state diff --git a/cdist/conf/type/__file/explorer/stat b/cdist/conf/type/__file/explorer/stat index 8a917556..29b3c8a3 100755 --- a/cdist/conf/type/__file/explorer/stat +++ b/cdist/conf/type/__file/explorer/stat @@ -1,6 +1,8 @@ #!/bin/sh # # 2013 Steven Armstrong (steven-cdist armstrong.cc) +# 2019 Nico Schottelius (nico-cdist at schottelius.org) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -20,37 +22,63 @@ destination="/$__object_id" +fallback() { + # Fallback: Patch the output together, manually. + + ls_line=$(ls -ldn "$destination") + + uid=$(echo "$ls_line" | awk '{ print $3 }') + gid=$(echo "$ls_line" | awk '{ print $4 }') + + owner=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/passwd) + group=$(awk -F: -v gid="$gid" '$3 == gid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/group) + + mode_text=$(echo "$ls_line" | awk '{ print $1 }') + mode=$(echo "$mode_text" | awk '{for(i=8;i>=0;--i){c=substr($1,10-i,1);k+=((c~/[rwxst]/)*2^i);if(!(i%3))k+=(tolower(c)~/[lst]/)*2^(9+i/3)}printf("%04o",k)}') + + size=$(echo "$ls_line" | awk '{ print $5 }') + links=$(echo "$ls_line" | awk '{ print $2 }') + + printf 'type: %s\nowner: %d %s\ngroup: %d %s\nmode: %s %s\nsize: %d\nlinks: %d\n' \ + "$("$__type_explorer/type")" \ + "$uid" "$owner" \ + "$gid" "$group" \ + "$mode" "$mode_text" \ + "$size" \ + "$links" +} + + # nothing to work with, nothing we could do [ -e "$destination" ] || exit 0 -os=$("$__explorer/os") -case "$os" in - "freebsd"|"netbsd"|"openbsd") - # FIXME: should be something like this based on man page, but can not test - stat -f "type: %ST + +command -v stat >/dev/null 2>&1 || { + fallback + exit +} + + +case $("$__explorer/os") +in + freebsd|netbsd|openbsd|macosx) + stat -f 'type: %HT owner: %Du %Su group: %Dg %Sg -mode: %Op %Sp +mode: %Mp%03Lp %Sp size: %Dz links: %Dl -" "$destination" - ;; - "macosx") - stat -f "type: %HT -owner: %Du %Su -group: %Dg %Sg -mode: %Lp %Sp -size: %Dz -links: %Dl -" "$destination" - ;; +' "$destination" | awk '/^type/ { print tolower($0); next } { print }' + ;; *) - stat --printf="type: %F + # NOTE: Do not use --printf here as it is not supported by BusyBox stat. + # NOTE: BusyBox's stat might not support the "-c" option, in which case + # we fall through to the shell fallback. + stat -c 'type: %F owner: %u %U group: %g %G -mode: %a %A +mode: %04a %A size: %s -links: %h -" "$destination" - ;; +links: %h' "$destination" 2>/dev/null || fallback + ;; esac diff --git a/cdist/conf/type/__file/gencode-local b/cdist/conf/type/__file/gencode-local index 15a9ee0e..231b6927 100755 --- a/cdist/conf/type/__file/gencode-local +++ b/cdist/conf/type/__file/gencode-local @@ -23,7 +23,7 @@ destination="/$__object_id" state_should="$(cat "$__object/parameter/state")" type="$(cat "$__object/explorer/type")" -[ "$state_should" = "exists" -a "$type" = "file" ] && exit 0 # nothing to do +[ "$state_should" = "exists" ] && [ "$type" = "file" ] && exit 0 # nothing to do if [ "$state_should" = "pre-exists" ]; then if [ -f "$__object/parameter/source" ]; then @@ -31,17 +31,29 @@ if [ "$state_should" = "pre-exists" ]; then exit 1 fi - if [ "$type" = "file" ]; then - exit 0 # nothing to do - else - echo "File \"$destination\" does not exist" - exit 1 - fi + case $type in + file) + # nothing to do + exit 0 + ;; + none) + printf 'File "%s" does not exist\n' "$destination" >&2 + exit 1 + ;; + directory|symlink) + printf 'File "%s" exists and is a %s, but should be a regular file\n' "$destination" "$type" >&2 + exit 1 + ;; + *) + printf 'File or directory "%s" is in an unknown state\n' "$destination" >&2 + exit 1 + ;; + esac fi upload_file= create_file= -if [ "$state_should" = "present" -o "$state_should" = "exists" ]; then +if [ "$state_should" = "present" ] || [ "$state_should" = "exists" ]; then if [ ! -f "$__object/parameter/source" ]; then remote_stat="$(cat "$__object/explorer/stat")" if [ -z "$remote_stat" ]; then @@ -70,7 +82,7 @@ if [ "$state_should" = "present" -o "$state_should" = "exists" ]; then fi fi fi - if [ "$create_file" -o "$upload_file" ]; then + if [ "$create_file" ] || [ "$upload_file" ]; then # tell gencode-remote that we created or uploaded a file and that it must # set all attributes no matter what the explorer retreived mkdir "$__object/files" @@ -84,7 +96,7 @@ DONE if [ "$upload_file" ]; then echo upload >> "$__messages_out" # IPv6 fix - if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$') + if echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$' then my_target_host="[${__target_host}]" else diff --git a/cdist/conf/type/__file/gencode-remote b/cdist/conf/type/__file/gencode-remote index c90be0be..f7a528fd 100755 --- a/cdist/conf/type/__file/gencode-remote +++ b/cdist/conf/type/__file/gencode-remote @@ -23,7 +23,7 @@ destination="/$__object_id" state_should="$(cat "$__object/parameter/state")" type="$(cat "$__object/explorer/type")" stat_file="$__object/explorer/stat" - +fire_onchange='' get_current_value() { if [ -s "$stat_file" ]; then @@ -43,52 +43,70 @@ get_current_value() { } set_group() { - echo chgrp \"$1\" \"$destination\" - echo chgrp $1 >> "$__messages_out" + echo "chgrp '$1' '$destination'" + echo "chgrp '$1'" >> "$__messages_out" + fire_onchange=1 } set_owner() { - echo chown \"$1\" \"$destination\" - echo chown $1 >> "$__messages_out" + echo "chown '$1' '$destination'" + echo "chown '$1'" >> "$__messages_out" + fire_onchange=1 } set_mode() { - echo chmod \"$1\" \"$destination\" - echo chmod $1 >> "$__messages_out" + echo "chmod '$1' '$destination'" + echo "chmod '$1'" >> "$__messages_out" + fire_onchange=1 } -set_attributes= case "$state_should" in - present|exists|pre-exists) - # Note: Mode - needs to happen last as a chown/chgrp can alter mode by - # clearing S_ISUID and S_ISGID bits (see chown(2)) - for attribute in group owner mode; do - if [ -f "$__object/parameter/$attribute" ]; then - value_should="$(cat "$__object/parameter/$attribute")" + present|exists) + # Note: Mode - needs to happen last as a chown/chgrp can alter mode by + # clearing S_ISUID and S_ISGID bits (see chown(2)) + for attribute in group owner mode; do + if [ -f "$__object/parameter/$attribute" ]; then + value_should="$(cat "$__object/parameter/$attribute")" - # change 0xxx format to xxx format => same as stat returns - if [ "$attribute" = mode ]; then - value_should="$(echo $value_should | sed 's/^0\(...\)/\1/')" - fi - - value_is="$(get_current_value "$attribute" "$value_should")" - if [ -f "$__object/files/set-attributes" -o "$value_should" != "$value_is" ]; then - "set_$attribute" "$value_should" + # format mode in four digits => same as stat returns + if [ "$attribute" = mode ]; then + # Convert to four-digit octal number (printf interprets + # strings with leading 0s as octal!) + value_should=$(printf '%04o' "0${value_should}") + fi + + value_is="$(get_current_value "$attribute" "$value_should")" + if [ -f "$__object/files/set-attributes" ] || [ "$value_should" != "$value_is" ]; then + "set_$attribute" "$value_should" + fi fi + done + if [ -f "$__object/files/set-attributes" ]; then + # set-attributes is created if file is created or uploaded in gencode-local + fire_onchange=1 fi - done - ;; absent) if [ "$type" = "file" ]; then - echo rm -f \"$destination\" + echo "rm -f '$destination'" echo remove >> "$__messages_out" + fire_onchange=1 fi ;; + pre-exists) + : + ;; + *) echo "Unknown state: $state_should" >&2 exit 1 ;; esac + +if [ -f "$__object/parameter/onchange" ]; then + if [ -n "$fire_onchange" ]; then + cat "$__object/parameter/onchange" + fi +fi diff --git a/cdist/conf/type/__file/man.rst b/cdist/conf/type/__file/man.rst index a141d70b..2f3b9e69 100644 --- a/cdist/conf/type/__file/man.rst +++ b/cdist/conf/type/__file/man.rst @@ -50,19 +50,22 @@ state create or modify it group - Group to chgrp to. + Group to chgrp to. Defaults to ``root``. mode - Unix permissions, suitable for chmod. + Unix permissions, suitable for chmod. Defaults to a very secure ``0600``. owner - User to chown to. + User to chown to. Defaults to ``root``. source If supplied, copy this file from the host running cdist to the target. If not supplied, an empty file or directory will be created. If source is '-' (dash), take what was written to stdin as the file content. +onchange + The code to run if file is modified. + MESSAGES -------- chgrp diff --git a/cdist/conf/type/__file/parameter/optional b/cdist/conf/type/__file/parameter/optional index c696d592..9b98352c 100644 --- a/cdist/conf/type/__file/parameter/optional +++ b/cdist/conf/type/__file/parameter/optional @@ -3,3 +3,4 @@ group mode owner source +onchange diff --git a/cdist/conf/type/__filesystem/explorer/lsblk b/cdist/conf/type/__filesystem/explorer/lsblk index 9ae544ac..9be3c575 100644 --- a/cdist/conf/type/__filesystem/explorer/lsblk +++ b/cdist/conf/type/__filesystem/explorer/lsblk @@ -18,16 +18,16 @@ # along with cdist. If not, see . # -os=$("$__explorer/os") +os=$("${__explorer:?}/os") -if [ -f "$__object/parameter/device" ]; then +if [ -f "${__object:?}/parameter/device" ]; then blkdev="$(cat "$__object/parameter/device")" else - blkdev="$__object_id" + blkdev="${__object_id:?}" fi case "$os" in - centos|fedora|redhat|suse|gentoo) + alpine|centos|fedora|redhat|suse|gentoo) if [ ! -x "$(command -v lsblk)" ]; then echo "lsblk is required for __filesystem type" >&2 exit 1 diff --git a/cdist/conf/type/__firewalld_rule/explorer/rule b/cdist/conf/type/__firewalld_rule/explorer/rule index 5a0e0265..0234e5b6 100644 --- a/cdist/conf/type/__firewalld_rule/explorer/rule +++ b/cdist/conf/type/__firewalld_rule/explorer/rule @@ -25,7 +25,7 @@ chain="$(cat "$__object/parameter/chain")" priority="$(cat "$__object/parameter/priority")" rule="$(cat "$__object/parameter/rule")" -if firewall-cmd --permanent --direct --query-rule "$protocol" "$table" "$chain" "$priority" $rule >/dev/null; then +if firewall-cmd --permanent --direct --query-rule "$protocol" "$table" "$chain" "$priority" "$rule" >/dev/null; then echo present else echo absent diff --git a/cdist/conf/type/__firewalld_rule/gencode-remote b/cdist/conf/type/__firewalld_rule/gencode-remote index 4c824d39..bd6d13e5 100755 --- a/cdist/conf/type/__firewalld_rule/gencode-remote +++ b/cdist/conf/type/__firewalld_rule/gencode-remote @@ -19,7 +19,6 @@ # # -name="$__object_id" state_should="$(cat "$__object/parameter/state")" state_is="$(cat "$__object/explorer/rule")" @@ -33,13 +32,13 @@ rule="$(cat "$__object/parameter/rule")" case "$state_should" in present) - echo firewall-cmd --quiet --permanent --direct --add-rule \"$protocol\" \"$table\" \"$chain\" \"$priority\" $rule - echo firewall-cmd --quiet --direct --add-rule \"$protocol\" \"$table\" \"$chain\" \"$priority\" $rule + echo "firewall-cmd --quiet --permanent --direct --add-rule '$protocol' '$table' '$chain' '$priority' $rule" + echo "firewall-cmd --quiet --direct --add-rule '$protocol' '$table' '$chain' '$priority' $rule" ;; absent) - echo firewall-cmd --quiet --permanent --direct --remove-rule \"$protocol\" \"$table\" \"$chain\" \"$priority\" $rule - echo firewall-cmd --quiet --direct --remove-rule \"$protocol\" \"$table\" \"$chain\" \"$priority\" $rule + echo "firewall-cmd --quiet --permanent --direct --remove-rule '$protocol' '$table' '$chain' '$priority' $rule" + echo "firewall-cmd --quiet --direct --remove-rule '$protocol' '$table' '$chain' '$priority' $rule" ;; *) echo "Unknown state $state_should" >&2 diff --git a/cdist/conf/type/__git/explorer/group b/cdist/conf/type/__git/explorer/group index 1308c710..3ddf9656 100644 --- a/cdist/conf/type/__git/explorer/group +++ b/cdist/conf/type/__git/explorer/group @@ -2,4 +2,4 @@ destination="/$__object_id/.git" -stat --print "%G" ${destination} 2>/dev/null || exit 0 +stat --print "%G" "${destination}" 2>/dev/null || exit 0 diff --git a/cdist/conf/type/__git/explorer/owner b/cdist/conf/type/__git/explorer/owner index 8c36b035..4c3cd431 100644 --- a/cdist/conf/type/__git/explorer/owner +++ b/cdist/conf/type/__git/explorer/owner @@ -2,4 +2,4 @@ destination="/$__object_id/.git" -stat --print "%U" ${destination} 2>/dev/null || exit 0 +stat --print "%U" "${destination}" 2>/dev/null || exit 0 diff --git a/cdist/conf/type/__git/gencode-remote b/cdist/conf/type/__git/gencode-remote index d0d0d4ed..ab22655f 100755 --- a/cdist/conf/type/__git/gencode-remote +++ b/cdist/conf/type/__git/gencode-remote @@ -19,43 +19,46 @@ # # -state_is="$(cat "$__object/explorer/state")" -owner_is="$(cat "$__object/explorer/owner")" -group_is="$(cat "$__object/explorer/group")" +state_is=$(cat "$__object/explorer/state") +owner_is=$(cat "$__object/explorer/owner") +group_is=$(cat "$__object/explorer/group") -state_should="$(cat "$__object/parameter/state")" +state_should=$(cat "$__object/parameter/state") -branch="$(cat "$__object/parameter/branch")" +branch=$(cat "$__object/parameter/branch") -source="$(cat "$__object/parameter/source")" +source=$(cat "$__object/parameter/source") destination="/$__object_id" -owner="$(cat "$__object/parameter/owner")" -group="$(cat "$__object/parameter/group")" -mode="$(cat "$__object/parameter/mode")" +owner=$(cat "$__object/parameter/owner") +group=$(cat "$__object/parameter/group") +mode=$(cat "$__object/parameter/mode") -[ "$state_should" = "$state_is" -a \ - "$owner" = "$owner_is" -a \ - "$group" = "$group_is" -a \ - -n "$mode" ] && exit 0 +[ -f "$__object/parameter/recursive" ] && recursive='--recurse-submodules' || recursive='' +[ -f "$__object/parameter/shallow" ] && shallow='--depth 1 --shallow-submodules' || shallow='' + +[ "$state_should" = "$state_is" ] \ + && [ "$owner" = "$owner_is" ] \ + && [ "$group" = "$group_is" ] \ + && [ -n "$mode" ] && exit 0 case $state_should in present) - if [ "$state_should" != "$state_is" ]; then - echo git clone --quiet --branch "$branch" "$source" "$destination" + echo git clone --quiet "$recursive" "$shallow" --branch "$branch" "$source" "$destination" fi - if [ \( -n "$owner" -a "$owner_is" != "$owner" \) -o \ - \( -n "$group" -a "$group_is" != "$group" \) ]; then + if { [ -n "$owner" ] && [ "$owner_is" != "$owner" ]; } || \ + { [ -n "$group" ] && [ "$group_is" != "$group" ]; }; then echo chown -R "${owner}:${group}" "$destination" fi if [ -n "$mode" ]; then echo chmod -R "$mode" "$destination" fi ;; - # Handled in manifest + absent) + # Handled in manifest ;; *) diff --git a/cdist/conf/type/__git/man.rst b/cdist/conf/type/__git/man.rst index 17e9c623..d3e15f25 100644 --- a/cdist/conf/type/__git/man.rst +++ b/cdist/conf/type/__git/man.rst @@ -35,6 +35,12 @@ mode owner User to chown to. +recursive + Passes the --recurse-submodules flag to git when cloning the repository. + +shallow + Sets --depth=1 and --shallow-submodules for cloning repositories with big history. + EXAMPLES -------- @@ -44,7 +50,7 @@ EXAMPLES __git /home/services/dokuwiki --source git://github.com/splitbrain/dokuwiki.git # Checkout cdist, stay on branch 2.1 - __git /home/nico/cdist --source git://github.com/ungleich/cdist.git --branch 2.1 + __git /home/nico/cdist --source git@code.ungleich.ch:ungleich-public/cdist.git --branch 2.1 AUTHORS diff --git a/cdist/conf/type/__git/parameter/boolean b/cdist/conf/type/__git/parameter/boolean new file mode 100644 index 00000000..d600d4ca --- /dev/null +++ b/cdist/conf/type/__git/parameter/boolean @@ -0,0 +1,2 @@ +recursive +shallow diff --git a/cdist/conf/type/__go_get/explorer/go-executable b/cdist/conf/type/__go_get/explorer/go-executable old mode 100644 new mode 100755 index 4c84ce07..87182282 --- a/cdist/conf/type/__go_get/explorer/go-executable +++ b/cdist/conf/type/__go_get/explorer/go-executable @@ -1,3 +1,6 @@ +#!/bin/sh +# shellcheck disable=SC1091 [ -f /etc/environment ] && . /etc/environment +# shellcheck disable=SC1091 [ -f /etc/profile ] && . /etc/profile go version 2>/dev/null || true diff --git a/cdist/conf/type/__golang_from_vendor/gencode-remote b/cdist/conf/type/__golang_from_vendor/gencode-remote index 1654978b..5200e9e3 100755 --- a/cdist/conf/type/__golang_from_vendor/gencode-remote +++ b/cdist/conf/type/__golang_from_vendor/gencode-remote @@ -2,7 +2,7 @@ version=$(cat "$__object/parameter/version") -kernel_name=$(cat "$__global/explorer/kernel_name" | tr '[:upper:]' '[:lower:]') +kernel_name=$(tr '[:upper:]' '[:lower:]' < "$__global/explorer/kernel_name") machine=$(cat "$__global/explorer/machine") case $machine in x86_64|amd64) diff --git a/cdist/conf/type/__golang_from_vendor/manifest b/cdist/conf/type/__golang_from_vendor/manifest index cf164524..ad39ddfb 100755 --- a/cdist/conf/type/__golang_from_vendor/manifest +++ b/cdist/conf/type/__golang_from_vendor/manifest @@ -1,3 +1,4 @@ #!/bin/sh -e +# shellcheck disable=SC2016 __line go_in_path --line 'export PATH=/usr/local/go/bin:$PATH' --file /etc/profile diff --git a/cdist/conf/type/__grafana_dashboard/manifest b/cdist/conf/type/__grafana_dashboard/manifest index e62bd15f..d145c4c3 100755 --- a/cdist/conf/type/__grafana_dashboard/manifest +++ b/cdist/conf/type/__grafana_dashboard/manifest @@ -1,16 +1,23 @@ #!/bin/sh -e -os=$(cat $__global/explorer/os) -os_version=$(cat $__global/explorer/os_version) +os=$(cat "$__global/explorer/os") +os_version=$(cat "$__global/explorer/os_version") +require="" case $os in debian|devuan) case $os_version in 8*|jessie) - apt_source_distribution=jessie + # Differntation not needed anymore + apt_source_distribution=stable ;; - 9*|ascii/ceres) - apt_source_distribution=stretch + 9*|ascii/ceres|ascii) + # Differntation not needed anymore + apt_source_distribution=stable + ;; + 10*) + # Differntation not needed anymore + apt_source_distribution=stable ;; *) echo "Don't know how to install Grafana on $os $os_version. Send us a pull request!" >&2 @@ -20,16 +27,15 @@ case $os in __apt_key_uri grafana \ --name 'Grafana Release Signing Key' \ - --uri https://packagecloud.io/gpg.key + --uri https://packages.grafana.com/gpg.key require="$require __apt_key_uri/grafana" __apt_source grafana \ - --uri https://packagecloud.io/grafana/stable/debian/ \ + --uri https://packages.grafana.com/oss/deb \ --distribution $apt_source_distribution \ --component main - __package apt-transport-https - - require="$require __apt_source/grafana __package/apt-transport-https" __package grafana + require="$require __apt_source/grafana" __apt_update_index + require="$require __package/apt-transport-https __apt_update_index" __package grafana require="$require __package/grafana" __start_on_boot grafana-server require="$require __start_on_boot/grafana-server" __process grafana-server --start "service grafana-server start" ;; diff --git a/cdist/conf/type/__group/explorer/group b/cdist/conf/type/__group/explorer/group index 07f73a91..dc673f61 100755 --- a/cdist/conf/type/__group/explorer/group +++ b/cdist/conf/type/__group/explorer/group @@ -1,6 +1,7 @@ #!/bin/sh # # 2011-2015 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -21,7 +22,21 @@ # Get an existing groups group entry. # +not_supported() { + echo "Your operating system ($("$__explorer/os")) is currently not supported." >&2 + echo "Cannot extract group information." >&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 +} + name=$__object_id -getent group "$name" || true - +if command -v getent >/dev/null +then + getent group "$name" || true +elif [ -f /etc/group ] +then + grep "^${name}:" /etc/group || true +else + not_supported +fi diff --git a/cdist/conf/type/__group/explorer/gshadow b/cdist/conf/type/__group/explorer/gshadow index 2e2ab29d..05841d69 100755 --- a/cdist/conf/type/__group/explorer/gshadow +++ b/cdist/conf/type/__group/explorer/gshadow @@ -1,6 +1,7 @@ #!/bin/sh # # 2011-2015 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -22,13 +23,28 @@ # name=$__object_id -os="$($__explorer/os)" +os=$("$__explorer/os") -case "$os" in - "freebsd"|"netbsd") - echo "$os does not have getent gshadow" - exit 0 - ;; +not_supported() { + echo "Your operating system ($os) is currently not supported." >&2 + echo "Cannot extract group information." >&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 +} + +case $os in + "freebsd"|"netbsd") + echo "$os does not have getent gshadow" >&2 + exit 0 + ;; esac -getent gshadow "$name" || true +if command -v getent >/dev/null +then + getent gshadow "$name" || true +elif [ -f /etc/gshadow ] +then + grep "^${name}:" /etc/gshadow || true +else + not_supported +fi diff --git a/cdist/conf/type/__group/gencode-remote b/cdist/conf/type/__group/gencode-remote index 5847cb66..ff63e218 100755 --- a/cdist/conf/type/__group/gencode-remote +++ b/cdist/conf/type/__group/gencode-remote @@ -30,9 +30,9 @@ state="$(cat "$__object/parameter/state")" # Use short option names for portability shorten_property() { case "$1" in - gid) echo "-g";; - password) echo "-p";; - system) echo "-r";; + gid) echo " -g";; + password) echo " -p";; + system) echo " -r";; esac } @@ -40,11 +40,9 @@ shorten_property() { if [ "$state" = "present" ]; then case "$os" in freebsd) - supported_add_properties="gid" supported_change_properties="gid" ;; *) - supported_add_properties="gid password system" supported_change_properties="gid password" ;; esac @@ -63,8 +61,8 @@ if [ "$state" = "present" ]; then ;; esac if [ "$new_value" != "$current_value" ]; then - set -- "$@" "$(shorten_property $property)" \'$new_value\' - echo change $property $new_value $current_value >> "$__messages_out" + set -- "$@" "$(shorten_property "$property")" \'"$new_value"\' + echo "change $property $new_value $current_value" >> "$__messages_out" fi fi done @@ -83,14 +81,14 @@ if [ "$state" = "present" ]; then new_value="$(cat "$__object/parameter/$property")" if [ -z "$new_value" ]; then # Boolean parameters have no value - set -- "$@" "$(shorten_property $property)" + set -- "$@" "$(shorten_property "$property")" else - set -- "$@" "$(shorten_property $property)" \'$new_value\' + set -- "$@" "$(shorten_property "$property")" \'"$new_value"\' fi fi done if [ "$os" = "freebsd" ]; then - echo pw groupadd "$@" "$name" + echo pw groupadd "$name" "$@" else echo groupadd "$@" "$name" fi diff --git a/cdist/conf/type/__hostname/explorer/has_hostnamectl b/cdist/conf/type/__hostname/explorer/has_hostnamectl index 9040023d..2f531f30 100755 --- a/cdist/conf/type/__hostname/explorer/has_hostnamectl +++ b/cdist/conf/type/__hostname/explorer/has_hostnamectl @@ -21,4 +21,4 @@ # Check whether system has hostnamectl # -command -v hostnamectl || true +command -v hostnamectl 2>/dev/null || true diff --git a/cdist/conf/type/__hostname/explorer/max_len b/cdist/conf/type/__hostname/explorer/max_len new file mode 100644 index 00000000..fb863949 --- /dev/null +++ b/cdist/conf/type/__hostname/explorer/max_len @@ -0,0 +1,10 @@ +#!/bin/sh -e + +command -v getconf >/dev/null || exit 0 + +val=$(getconf HOST_NAME_MAX 2>/dev/null) || exit 0 + +if test -n "${val}" -a "${val}" != 'undefined' +then + echo "${val}" +fi diff --git a/cdist/conf/type/__hostname/gencode-remote b/cdist/conf/type/__hostname/gencode-remote index 89bf7b3f..c1a97ac8 100755 --- a/cdist/conf/type/__hostname/gencode-remote +++ b/cdist/conf/type/__hostname/gencode-remote @@ -2,6 +2,7 @@ # # 2014-2017 Steven Armstrong (steven-cdist at armstrong.cc) # 2014 Nico Schottelius (nico-cdist at schottelius.org) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,60 +20,86 @@ # along with cdist. If not, see . # -if [ -f "$__object/parameter/name" ]; then - name_should="$(cat "$__object/parameter/name")" +os=$(cat "${__global:?}/explorer/os") +name_running=$(cat "${__global:?}/explorer/hostname") +has_hostnamectl=$(cat "${__object:?}/explorer/has_hostnamectl") + + +if test -s "${__object:?}/parameter/name" +then + name_should=$(cat "${__object:?}/parameter/name") else - name_should="$(echo "${__target_host%%.*}")" + case ${os} + in + # RedHat-derivatives and BSDs + (centos|fedora|redhat|scientific|freebsd|macosx|netbsd|openbsd) + # Hostname is FQDN + name_should=${__target_host:?} + ;; + (*) + # Hostname is only first component of FQDN + name_should=${__target_host:?} + name_should=${name_should%%.*} + ;; + esac fi -os=$(cat "$__global/explorer/os") -name_running=$(cat "$__global/explorer/hostname") -name_config=$(cat "$__object/explorer/hostname_file") -name_sysconfig=$(cat "$__object/explorer/hostname_sysconfig") -has_hostnamectl=$(cat "$__object/explorer/has_hostnamectl") ################################################################################ -# If everything is ok -> exit +# Check if the (running) hostname is already correct # -case "$os" in - archlinux|debian|suse|ubuntu|devuan|coreos) - if [ "$name_config" = "$name_should" -a "$name_running" = "$name_should" ]; then - exit 0 - fi - ;; - scientific|centos|freebsd|openbsd) - if [ "$name_sysconfig" = "$name_should" -a "$name_running" = "$name_should" ]; then - exit 0 - fi - ;; - *) - echo "Unsupported os: $os" >&2 - exit 1 - ;; -esac +test "${name_running}" != "${name_should}" || exit 0 + ################################################################################ # Setup hostname # -echo changed >> "$__messages_out" +echo 'changed' >>"${__messages_out:?}" -# Use the good old way to set the hostname even on machines running systemd. -case "$os" in - archlinux|debian|ubuntu|devuan|centos|coreos) - echo "printf '%s\n' '$name_should' > /etc/hostname" - echo "hostname -F /etc/hostname" - ;; - freebsd|openbsd) - echo "hostname '$name_should'" - ;; - suse) - echo "hostname '$name_should'" - echo "printf '%s\n' '$name_should' > /etc/HOSTNAME" - ;; +# Use the good old way to set the hostname. +case ${os} +in + (alpine|debian|devuan|ubuntu) + echo 'hostname -F /etc/hostname' + ;; + (archlinux) + echo 'command -v hostnamectl >/dev/null 2>&1' \ + "&& hostnamectl set-hostname '${name_should}'" \ + "|| hostname '${name_should}'" + ;; + (centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|gentoo|void) + echo "hostname '${name_should}'" + ;; + (openwrt) + echo "echo '${name_should}' >/proc/sys/kernel/hostname" + ;; + (macosx) + echo "scutil --set HostName '${name_should}'" + ;; + (solaris) + echo "uname -S '${name_should}'" + ;; + (slackware|suse) + # We do not read from /etc/HOSTNAME, because the running + # hostname is the first component only while the file contains + # the FQDN. + echo "hostname '${name_should}'" + ;; + (*) + # Fall back to set the hostname using hostnamectl, if available. + if test -n "${has_hostnamectl}" + then + # Don't use hostnamectl as the primary means to set the hostname for + # systemd systems, because it cannot be trusted to work reliably and + # exit with non-zero when it fails (e.g. hostname too long, + # D-Bus failure, etc.). + + echo "hostnamectl set-hostname \"\$(cat /etc/hostname)\"" + echo "test \"\$(hostname)\" = \"\$(cat /etc/hostname)\"" \ + " || hostname -F /etc/hostname" + else + printf "echo 'Unsupported OS: %s' >&2\n" "${os}" + printf 'exit 1\n' + fi + ;; esac - -if [ "$has_hostnamectl" ]; then - # Allow hostnamectl set-hostname to fail silently. - # Who the fuck invented a tool that needs dbus to set the hostname anyway ... - echo "hostnamectl set-hostname '$name_should' || true" -fi diff --git a/cdist/conf/type/__hostname/man.rst b/cdist/conf/type/__hostname/man.rst index d23a3b8a..72aefbab 100644 --- a/cdist/conf/type/__hostname/man.rst +++ b/cdist/conf/type/__hostname/man.rst @@ -8,7 +8,10 @@ cdist-type__hostname - Set the hostname DESCRIPTION ----------- -Set's the hostname on various operating systems. +Sets the hostname on various operating systems. + +**Tip:** For advice on choosing a hostname, see +`RFC 1178 `_. REQUIRED PARAMETERS @@ -18,7 +21,7 @@ None. OPTIONAL PARAMETERS ------------------- name - The hostname to set. Defaults to the first segment of __target_host + The hostname to set. Defaults to the first segment of __target_host (${__target_host%%.*}) diff --git a/cdist/conf/type/__hostname/manifest b/cdist/conf/type/__hostname/manifest index 74664aa7..b80aa2ef 100755 --- a/cdist/conf/type/__hostname/manifest +++ b/cdist/conf/type/__hostname/manifest @@ -2,6 +2,7 @@ # # 2012 Steven Armstrong (steven-cdist at armstrong.cc) # 2014 Nico Schottelius (nico-cdist at schottelius.org) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,50 +20,176 @@ # along with cdist. If not, see . # -os=$(cat "$__global/explorer/os") -if [ -f "$__object/parameter/name" ]; then - name_should="$(cat "$__object/parameter/name")" +set_hostname_systemd() { + echo "$1" | __file /etc/hostname --source - +} + +os=$(cat "${__global:?}/explorer/os") + +max_len=$(cat "${__object:?}/explorer/max_len") +has_hostnamectl=$(cat "${__object:?}/explorer/has_hostnamectl") + +if test -s "${__object:?}/parameter/name" +then + name_should=$(cat "${__object:?}/parameter/name") else - case "$os" in - openbsd) - name_should="$(echo "${__target_host}")" - ;; - *) - name_should="$(echo "${__target_host%%.*}")" - ;; + case ${os} + in + # RedHat-derivatives and BSDs + (centos|fedora|redhat|scientific|freebsd|netbsd|openbsd|slackware|suse) + # Hostname is FQDN + name_should=${__target_host:?} + ;; + *) + # Hostname is only first component of FQDN on all other systems. + name_should=${__target_host:?} + name_should=${name_should%%.*} + ;; esac fi +if test -n "${max_len}" && test "$(printf '%s' "${name_should}" | wc -c)" -gt "${max_len}" +then + printf "Host name too long. Up to %u characters allowed.\n" "${max_len}" >&2 + exit 1 +fi -not_supported() { - echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2 - echo "Please contribute an implementation for it if you can." >&2 - exit 1 -} +case ${os} +in + (alpine|debian|devuan|ubuntu|void) + echo "${name_should}" | __file /etc/hostname --source - + ;; + (archlinux) + if test -n "${has_hostnamectl}" + then + set_hostname_systemd "${name_should}" + else + echo 'Ancient ArchLinux variants without hostnamectl are not supported.' >&2 + exit 1 + # Only for ancient ArchLinux, write to /etc/rc.conf on pre-systemd + # versions. There are some versions which use /etc/hostname but not + # systemd. It is unclear which ones these are. -case "$os" in - archlinux|debian|suse|ubuntu|devuan|coreos) - # handled in gencode-remote - : - ;; - scientific|centos) - __key_value sysconfig-hostname \ - --file /etc/sysconfig/network \ - --delimiter '=' \ - --key HOSTNAME \ - --value "$name_should" --exact_delimiter - ;; - freebsd) - __key_value rcconf-hostname \ + # __key_value '/etc/rc.conf:HOSTNAME' \ + # --file /etc/rc.conf \ + # --delimiter '=' --exact_delimiter \ + # --key 'HOSTNAME' \ + # --value "\"$name_should\"" + fi + ;; + (centos|fedora|redhat|scientific) + if test -z "${has_hostnamectl}" + then + # Only write to /etc/sysconfig/network on non-systemd versions. + # On systemd-based versions this entry is ignored. + __key_value '/etc/sysconfig/network:HOSTNAME' \ + --file /etc/sysconfig/network \ + --delimiter '=' --exact_delimiter \ + --key HOSTNAME \ + --value "\"${name_should}\"" + else + set_hostname_systemd "${name_should}" + fi + ;; + (gentoo) + # Only write to /etc/conf.d/hostname on OpenRC-based installations. + # On systemd use hostnamectl(1) in gencode-remote. + if test -z "${has_hostnamectl}" + then + __key_value '/etc/conf.d/hostname:hostname' \ + --file /etc/conf.d/hostname \ + --delimiter '=' --exact_delimiter \ + --key 'hostname' \ + --value "\"${name_should}\"" + else + set_hostname_systemd "$name_should" + fi + ;; + (freebsd) + __key_value '/etc/rc.conf:hostname' \ --file /etc/rc.conf \ - --delimiter '=' \ + --delimiter '=' --exact_delimiter \ --key 'hostname' \ - --value "$name_should" - ;; - openbsd) - echo "$name_should" | __file /etc/myname --source - - ;; - *) - not_supported - ;; + --value "\"${name_should}\"" + ;; + (macosx) + # handled in gencode-remote + ;; + (netbsd) + __key_value '/etc/rc.conf:hostname' \ + --file /etc/rc.conf \ + --delimiter '=' --exact_delimiter \ + --key 'hostname' \ + --value "\"${name_should}\"" + + # To avoid confusion, ensure that the hostname is only stored once. + __file /etc/myname --state absent + ;; + (openbsd) + echo "${name_should}" | __file /etc/myname --source - + ;; + (openwrt) + __uci system.@system[0].hostname --value "${name_should}" + # --transaction hostname + ;; + (slackware) + # We write the FQDN into /etc/HOSTNAME. But /etc/rc.d/rc.M will only + # read the first component from this file and set it as the running + # hostname on boot. + echo "${name_should}" | __file /etc/HOSTNAME --source - + ;; + (solaris) + echo "${name_should}" | __file /etc/nodename --source - + ;; + (suse) + if test -s "${__global:?}/explorer/os_release" + then + # shellcheck source=/dev/null + os_version=$(. "${__global:?}/explorer/os_release" && echo "${VERSION}") + else + os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global:?}/explorer/os_version") + fi + os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)') + + # Classic SuSE stores the FQDN in /etc/HOSTNAME, while + # systemd does not. The running hostname is the first + # component in both cases. + # In versions before 15.x, the FQDN is stored in /etc/hostname. + if test -n "${has_hostnamectl}" \ + && test "${os_major}" -ge 15 \ + && test "${os_major}" -ne 42 + then + # strip away everything but the first part from $name_should + name_should=${name_should%%.*} + fi + + # Modern SuSE provides /etc/HOSTNAME as a symlink for + # backwards-compatibility. Unfortunately it cannot be used + # here as __file does not follow the symlink. + # Therefore, we use the presence of the hostnamectl binary as + # an indication of which file to use. This unfortunately does + # not work correctly on openSUSE 12.x which provides + # hostnamectl but not /etc/hostname. + + if test -n "${has_hostnamectl}" -a "${os_major}" -gt 12 + then + hostname_file=/etc/hostname + else + hostname_file=/etc/HOSTNAME + fi + + echo "${name_should}" | __file "${hostname_file}" --source - + ;; + (*) + # On other operating systems we fall back to systemd's + # hostnamectl if available… + if test -n "${has_hostnamectl}" + then + set_hostname_systemd "${name_should}" + else + echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 + fi + ;; esac diff --git a/cdist/conf/type/__hosts/man.rst b/cdist/conf/type/__hosts/man.rst index bece7967..1ac706cb 100644 --- a/cdist/conf/type/__hosts/man.rst +++ b/cdist/conf/type/__hosts/man.rst @@ -25,6 +25,10 @@ ip state is ``present``, this parameter is mandatory, if state is ``absent``, this parameter is silently ignored. +alias + An alias for the hostname. + This parameter can be specified multiple times (once per alias). + EXAMPLES -------- @@ -36,6 +40,8 @@ EXAMPLES # previously configured via __hosts. __hosts happy --state absent + __hosts srv1.example.com --ip 192.168.0.42 --alias srv1 + SEE ALSO -------- @@ -43,13 +49,14 @@ SEE ALSO AUTHORS ------- - -Dmitry Bogatov +| Dmitry Bogatov +| Dennis Camera COPYING ------- -Copyright (C) 2015,2016 Dmitry Bogatov. Free use of this software is granted -under the terms of the GNU General Public License version 3 or later -(GPLv3+). +Copyright \(C) 2015-2016 Dmitry Bogatov, 2019 Dennis Camera. +You can redistribute it and/or modify it under the terms of the GNU General +Public License as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. diff --git a/cdist/conf/type/__hosts/manifest b/cdist/conf/type/__hosts/manifest index c536b83b..8103ebd5 100755 --- a/cdist/conf/type/__hosts/manifest +++ b/cdist/conf/type/__hosts/manifest @@ -1,29 +1,42 @@ #!/bin/sh -e -# Copyright (C) 2015 Bogatov Dmitry # -# This program is free software: you can redistribute it and/or modify +# Copyright (C) 2015 Bogatov Dmitry +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # -# This program is distributed in the hope that it will be useful, +# cdist is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set -ue +# -hostname="$__object_id" -state="$(cat "$__object/parameter/state")" -marker="# __hosts/$hostname" +set -e -set -- "__hosts/$hostname" --file /etc/hosts --state "$state" +hostname=$__object_id +state=$(cat "${__object}/parameter/state") +marker="# __hosts/${hostname}" -if [ "$state" = absent ] ; then - __line "$@" --regex "$marker" +if test "${state}" != 'absent' +then + ip=$(cat "${__object}/parameter/ip") + if test -s "${__object}/parameter/alias" + then + aliases=$(while read -r a; do printf '\t%s' "$a"; done <"$__object/parameter/alias") + fi + + set -- --line "$(printf '%s\t%s%s %s' \ + "${ip}" "${hostname}" "${aliases}" "${marker}")" else - ip="$(cat "$__object/parameter/ip")" - __line "$@" --line "$ip $hostname $marker" + set -- --regex "$(echo "${marker}" | sed -e 's/\./\\./')$" fi + +__line "/etc/hosts:${hostname}" --file /etc/hosts --state "${state}" "$@" diff --git a/cdist/conf/type/__hosts/parameter/optional_multiple b/cdist/conf/type/__hosts/parameter/optional_multiple new file mode 100644 index 00000000..d077ed80 --- /dev/null +++ b/cdist/conf/type/__hosts/parameter/optional_multiple @@ -0,0 +1 @@ +alias diff --git a/cdist/conf/type/__hwclock/explorer/adjtime_mode b/cdist/conf/type/__hwclock/explorer/adjtime_mode new file mode 100755 index 00000000..2b27bedc --- /dev/null +++ b/cdist/conf/type/__hwclock/explorer/adjtime_mode @@ -0,0 +1,28 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Prints the clock mode read from the /etc/adjtime file, if present. +# + +# not all operating systems use an adjfile +test -f /etc/adjtime || exit 0 + +# 3rd line is clock mode +# adjtime(5) https://man7.org/linux/man-pages/man5/adjtime.5.html +sed -n 3p /etc/adjtime diff --git a/cdist/conf/type/__pf_apply/explorer/rcvar b/cdist/conf/type/__hwclock/explorer/timedatectl_localrtc similarity index 63% rename from cdist/conf/type/__pf_apply/explorer/rcvar rename to cdist/conf/type/__hwclock/explorer/timedatectl_localrtc index 20e9dfcc..8239122e 100755 --- a/cdist/conf/type/__pf_apply/explorer/rcvar +++ b/cdist/conf/type/__hwclock/explorer/timedatectl_localrtc @@ -1,6 +1,6 @@ -#!/bin/sh +#!/bin/sh -e # -# 2012 Jake Guffey (jake.guffey at eprotex.com) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -17,20 +17,11 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# Get the location of the pf ruleset on the target host. +# Prints the LocalRTC property using timedatectl on systemd-based systems. # -# Debug -#exec >&2 -#set -x - -# Check /etc/rc.conf for pf's configuration file name. Default to /etc/pf.conf - -RC="/etc/rc.conf" -PFCONF="$(grep '^pf_rules=' ${RC} | cut -d= -f2 | sed 's/"//g')" -echo ${PFCONF:-"/etc/pf.conf"} - -# Debug -#set +x +command -v timedatectl >/dev/null 2>&1 || exit 0 +# NOTE: Older versions of timedatectl do not support `timedatectl show' +timedatectl --no-pager status \ +| awk -F': ' '$1 ~ "RTC in local TZ$" { sub(/[ \t]*$/, "", $2); print $2 }' diff --git a/cdist/conf/type/__hwclock/gencode-remote b/cdist/conf/type/__hwclock/gencode-remote new file mode 100755 index 00000000..5995fb23 --- /dev/null +++ b/cdist/conf/type/__hwclock/gencode-remote @@ -0,0 +1,62 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +mode=$(cat "${__object:?}/parameter/mode") + +timedatectl_localrtc=$(cat "${__object:?}/explorer/timedatectl_localrtc") +adjtime_mode=$(cat "${__object:?}/explorer/adjtime_mode") + + +case ${mode} +in + (localtime) + adjtime_str=LOCAL + local_rtc_str=yes + ;; + (UTC|utc) + adjtime_str=UTC + local_rtc_str=no + ;; + (*) + printf 'Invalid value for --mode: %s\n' "${mode}" >&2 + printf 'Acceptable values are: localtime, utc.\n' >&2 + exit 1 +esac + + +if test -n "${timedatectl_localrtc}" +then + # systemd + timedatectl_should=${local_rtc_str} + if test "${timedatectl_localrtc}" != "${timedatectl_should}" + then + printf 'timedatectl set-local-rtc %s\n' "${timedatectl_should}" + fi +elif test -n "${adjtime_mode}" +then + # others (update /etc/adjtime if present) + if test "${adjtime_mode}" != "${adjtime_str}" + then + # Update /etc/adjtime (3rd line is clock mode) + # adjtime(5) https://man7.org/linux/man-pages/man5/adjtime.5.html + # FIXME: Should maybe add third line if adjfile only contains two lines + printf "sed -i '3c\\\\\\n%s\\n' /etc/adjtime\\n" "${adjtime_str}" + fi +fi diff --git a/cdist/conf/type/__hwclock/man.rst b/cdist/conf/type/__hwclock/man.rst new file mode 100644 index 00000000..65eb648f --- /dev/null +++ b/cdist/conf/type/__hwclock/man.rst @@ -0,0 +1,63 @@ +cdist-type__hwclock(7) +====================== + +NAME +---- +cdist-type__hwclock - Manage the hardware real time clock. + + +DESCRIPTION +----------- +This type can be used to control how the hardware clock is used by the operating +system. + + +REQUIRED PARAMETERS +------------------- +mode + What mode the hardware clock is in. + + Acceptable values: + + localtime + The hardware clock is set to local time (common for systems also running + Windows.) + UTC + The hardware clock is set to UTC (common on UNIX systems.) + + +OPTIONAL PARAMETERS +------------------- +None. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Make the operating system treat the time read from the hwclock as UTC. + __hwclock --mode UTC + + +SEE ALSO +-------- +:strong:`hwclock`\ (8) + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__hwclock/manifest b/cdist/conf/type/__hwclock/manifest new file mode 100755 index 00000000..7d9ab88f --- /dev/null +++ b/cdist/conf/type/__hwclock/manifest @@ -0,0 +1,222 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +# TODO: Consider supporting BADYEAR + +os=$(cat "${__global:?}/explorer/os") +mode=$(cat "${__object:?}/parameter/mode") + +has_systemd_timedatectl=$(test -s "${__object:?}/explorer/timedatectl_localrtc" && echo true || echo false) + + +case ${mode} +in + (localtime) + local_clock=true + ;; + (UTC|utc) + local_clock=false + ;; + (*) + printf 'Invalid value for --mode: %s\n' "${mode}" >&2 + printf 'Acceptable values are: UTC, localtime.\n' >&2 + exit 1 +esac + + +case ${os} +in + (alpine|gentoo) + if ! $has_systemd_timedatectl + then + # NOTE: Gentoo also supports systemd, in which case /etc/conf.d is + # not used. So we check for systemd presence here and only + # update /etc/conf.d if systemd is not installed. + # https://wiki.gentoo.org/wiki/System_time#Hardware_clock + + export CDIST_ORDER_DEPENDENCY=true + __file /etc/conf.d/hwclock --state present \ + --owner root --group root --mode 0644 + __key_value /etc/conf.d/hwclock:clock \ + --file /etc/conf.d/hwclock \ + --key clock \ + --delimiter '=' --exact_delimiter \ + --value "\"$($local_clock && echo local || echo UTC)\"" + unset CDIST_ORDER_DEPENDENCY + fi + ;; + (centos|fedora|redhat|scientific) + os_version=$(cat "${__global:?}/explorer/os_version") + os_major=$(expr "${os_version}" : '.* release \([0-9]*\)') + case ${os} + in + (centos|scientific) + update_sysconfig=$(test "${os_major}" -lt 6 && echo true || echo false) + ;; + (fedora) + update_sysconfig=$(test "${os_major}" -lt 10 && echo true || echo false) + ;; + (redhat|*) + case ${os_version} + in + ('Red Hat Enterprise Linux'*) + update_sysconfig=$(test "${os_major}" -lt 6 && echo true || echo false) + ;; + ('Red Hat Linux'*) + update_sysconfig=true + ;; + (*) + printf 'Could not determine Red Hat distribution.\n' >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + ;; + esac + ;; + esac + + if ${update_sysconfig:?} + then + export CDIST_ORDER_DEPENDENCY=true + __file /etc/sysconfig/clock --state present \ + --owner root --group root --mode 0644 + __key_value /etc/sysconfig/clock:UTC \ + --file /etc/sysconfig/clock \ + --key UTC \ + --delimiter '=' --exact_delimiter \ + --value "$($local_clock && echo false || echo true)" + unset CDIST_ORDER_DEPENDENCY + fi + ;; + (debian|devuan|ubuntu) + os_major=$(sed 's/[^0-9].*$//' "${__global:?}/explorer/os_version") + + case ${os} + in + (debian) + if test "${os_major}" -ge 7 + then + update_rcS=false + elif test "${os_major}" -ge 3 + then + update_rcS=true + else + # Debian 2.2 should be supportable using rcS. + # Debian 2.1 uses the ancient GMT key. + # Debian 1.3 does not have rcS. + printf "Your operating system (Debian %s) is currently not supported by this type (%s)\n" \ + "$(cat "${__global:?}/explorer/os_version")" "${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + fi + ;; + (devuan) + update_rcS=false + ;; + (ubuntu) + update_rcS=$(test "${os_major}" -lt 16 && echo true || echo false) + ;; + esac + + if ${update_rcS} + then + export CDIST_ORDER_DEPENDENCY=true + __file /etc/default/rcS --state present \ + --owner root --group root --mode 0644 + __key_value /etc/default/rcS:UTC \ + --file /etc/default/rcS \ + --key UTC \ + --delimiter '=' --exact_delimiter \ + --value "$($local_clock && echo no || echo yes)" + unset CDIST_ORDER_DEPENDENCY + fi + ;; + (freebsd) + # cf. adjkerntz(8) + __file /etc/wall_cmos_clock \ + --state "$($local_clock && echo present || echo absent)" \ + --owner root --group wheel --mode 0444 + ;; + (netbsd) + # https://wiki.netbsd.org/guide/boot/#index9h2 + __key_value /etc/rc.conf:rtclocaltime \ + --file /etc/rc.conf \ + --key rtclocaltime \ + --delimiter '=' --exact_delimiter \ + --value "$($local_clock && echo YES || echo NO)" + ;; + (slackware) + __file /etc/hardwareclock --owner root --group root --mode 0644 \ + --source - <<-EOF + # /etc/hardwareclock + # + # Tells how the hardware clock time is stored. + # This file is managed by cdist. + + $($local_clock && echo localtime || echo UTC) + EOF + ;; + (suse) + if test -s "${__global:?}/explorer/os_release" + then + # shellcheck source=/dev/null + os_version=$(. "${__global:?}/explorer/os_release" && echo "${VERSION}") + else + os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global:?}/explorer/os_version") + fi + os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)') + + # TODO: Consider using `yast2 timezone set hwclock' instead + if expr "${os_major}" \< 12 + then + # Starting with SuSE 12 (first systemd-based version) + # /etc/sysconfig/clock does not contain the HWCLOCK line + # anymore. + # With SuSE 13, it has been reduced to TIMEZONE configuration. + __key_value /etc/sysconfig/clock:HWCLOCK \ + --file /etc/sysconfig/clock \ + --delimiter '=' --exact_delimiter \ + --key HWCLOCK \ + --value "$($local_clock && echo '"--localtime"' || echo '"-u"')" + fi + ;; + (void) + export CDIST_ORDER_DEPENDENCY=true + __file /etc/rc.conf \ + --owner root --group root --mode 0644 \ + --state present + __key_value /etc/rc.conf:HARDWARECLOCK \ + --file /etc/rc.conf \ + --delimiter '=' --exact_delimiter \ + --key HARDWARECLOCK \ + --value "\"$($local_clock && echo localtime || echo UTC)\"" + unset CDIST_ORDER_DEPENDENCY + ;; + (*) + if ! $has_systemd_timedatectl + then + printf "Your operating system (%s) is currently not supported by this type (%s)\n" "$os" "${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + fi + ;; +esac + +# NOTE: timedatectl set-local-rtc for systemd is in gencode-remote +# NOTE: /etc/adjtime is also updated in gencode-remote diff --git a/cdist/conf/type/__hwclock/parameter/required b/cdist/conf/type/__hwclock/parameter/required new file mode 100644 index 00000000..17ab372f --- /dev/null +++ b/cdist/conf/type/__hwclock/parameter/required @@ -0,0 +1 @@ +mode diff --git a/cdist/conf/type/__hwclock/singleton b/cdist/conf/type/__hwclock/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__install_bootloader_grub/gencode-remote b/cdist/conf/type/__install_bootloader_grub/gencode-remote index 6e6e5e85..1caebbbf 100755 --- a/cdist/conf/type/__install_bootloader_grub/gencode-remote +++ b/cdist/conf/type/__install_bootloader_grub/gencode-remote @@ -28,7 +28,7 @@ install_script="$__object/files/install_script" # Link file descriptor #6 with stdout exec 6>&1 # Link stdout with $install_script -exec > $install_script +exec > "$install_script" # Generate script to install bootloader on distro printf '#!/bin/sh -l\n' diff --git a/cdist/conf/type/__install_chroot_umount/manifest b/cdist/conf/type/__install_chroot_umount/manifest new file mode 120000 index 00000000..f17af67a --- /dev/null +++ b/cdist/conf/type/__install_chroot_umount/manifest @@ -0,0 +1 @@ +../__chroot_umount/manifest \ No newline at end of file diff --git a/cdist/conf/type/__install_config/files/remote/copy b/cdist/conf/type/__install_config/files/remote/copy index 15c901f9..fa7fa9b7 100755 --- a/cdist/conf/type/__install_config/files/remote/copy +++ b/cdist/conf/type/__install_config/files/remote/copy @@ -37,10 +37,12 @@ code="$(echo "$@" | sed "s|$target_host:|$target_host:$chroot|g")" log "target_host: $target_host" log "chroot: $chroot" -log "@: $@" +log "@: $*" log "code: $code" # copy files into chroot +# __default_remote_copy and code should be split +# shellcheck disable=SC2086 $__default_remote_copy $code log "-----" diff --git a/cdist/conf/type/__install_config/files/remote/exec b/cdist/conf/type/__install_config/files/remote/exec index 5b25e41e..c2057ebf 100755 --- a/cdist/conf/type/__install_config/files/remote/exec +++ b/cdist/conf/type/__install_config/files/remote/exec @@ -36,14 +36,17 @@ shift # escape ' with '"'"' code="$(echo "$@" | sed -e "s/'/'\"'\"'/g")" +# shellcheck disable=SC2089 code="chroot $chroot sh -e -c '$code'" log "target_host: $target_host" log "chroot: $chroot" -log "@: $@" +log "@: $*" log "code: $code" # Run the code -$__default_remote_exec $target_host $code +# __default_remote_exec and code should be split +# shellcheck disable=SC2086,SC2090 +$__default_remote_exec "$target_host" $code log "-----" diff --git a/cdist/conf/type/__install_config/gencode-local b/cdist/conf/type/__install_config/gencode-local index 8f24cf2e..dd4f2a78 100755 --- a/cdist/conf/type/__install_config/gencode-local +++ b/cdist/conf/type/__install_config/gencode-local @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2011-2017 Steven Armstrong (steven-cdist at armstrong.cc) +# 2011-2018 Steven Armstrong (steven-cdist at armstrong.cc) # # This file is part of cdist. # @@ -23,6 +23,7 @@ remote_exec="$__type/files/remote/exec" remote_copy="$__type/files/remote/copy" cat << DONE +export __cdist_install_config=yes export __cdist_log_level=$__cdist_log_level export __default_remote_exec="$__remote_exec" export __default_remote_copy="$__remote_copy" diff --git a/cdist/conf/type/__install_coreos/gencode-remote b/cdist/conf/type/__install_coreos/gencode-remote new file mode 100755 index 00000000..f550b5a5 --- /dev/null +++ b/cdist/conf/type/__install_coreos/gencode-remote @@ -0,0 +1,19 @@ +#!/bin/sh -e + +device=$(cat "${__object:?}/parameter/device") +ignition=$(cat "${__object}/parameter/ignition") + +cat < "\${ignition_file}" << eof +$(base64 "${ignition}") +eof + +coreos-install -d "${device}" \ + \$(if [ -s "\${ignition_file}" ]; then + printf -- "-i \${ignition_file}\\n" + fi) + +rm "\${ignition_file}" +EOF diff --git a/cdist/conf/type/__install_coreos/install b/cdist/conf/type/__install_coreos/install new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__install_coreos/man.rst b/cdist/conf/type/__install_coreos/man.rst new file mode 100644 index 00000000..314f9f2a --- /dev/null +++ b/cdist/conf/type/__install_coreos/man.rst @@ -0,0 +1,50 @@ +cdist-type__install_coreos(7) +============================= + +NAME +---- + +cdist-type__install_coreos - Install CoreOS + +DESCRIPTION +----------- + +This type installs CoreOS to a given device using coreos-install_, which is +present in CoreOS ISO by default. + +.. _coreos-install: https://raw.githubusercontent.com/coreos/init/master/bin/coreos-install + +REQUIRED PARAMETERS +------------------- + +device + A device CoreOS will be installed to. + +OPTIONAL PARAMETERS +------------------- + +ignition + Path to ignition config. + +EXAMPLES +-------- + +.. code-block:: sh + + __install_coreos \ + --device /dev/sda \ + --ignition ignition.json + + +AUTHORS +------- + +Ľubomír Kučera + +COPYING +------- + +Copyright \(C) 2018 Ľubomír Kučera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__install_coreos/parameter/default/ignition b/cdist/conf/type/__install_coreos/parameter/default/ignition new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__install_coreos/parameter/optional b/cdist/conf/type/__install_coreos/parameter/optional new file mode 100644 index 00000000..df284caa --- /dev/null +++ b/cdist/conf/type/__install_coreos/parameter/optional @@ -0,0 +1 @@ +ignition diff --git a/cdist/conf/type/__install_coreos/parameter/required b/cdist/conf/type/__install_coreos/parameter/required new file mode 100644 index 00000000..f89ee6a8 --- /dev/null +++ b/cdist/conf/type/__install_coreos/parameter/required @@ -0,0 +1 @@ +device diff --git a/cdist/conf/type/__install_coreos/singleton b/cdist/conf/type/__install_coreos/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__install_directory/man.rst b/cdist/conf/type/__install_directory/man.rst deleted file mode 120000 index 1ad7fa84..00000000 --- a/cdist/conf/type/__install_directory/man.rst +++ /dev/null @@ -1 +0,0 @@ -../__directory/man.rst \ No newline at end of file diff --git a/cdist/conf/type/__install_directory/man.rst b/cdist/conf/type/__install_directory/man.rst new file mode 100644 index 00000000..c402cbad --- /dev/null +++ b/cdist/conf/type/__install_directory/man.rst @@ -0,0 +1,101 @@ +cdist-type__install_directory(7) +================================ + +NAME +---- +cdist-type__install_directory - Manage a directory with install command + + +DESCRIPTION +----------- +This cdist type allows you to create or remove directories on the target. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +state + 'present' or 'absent', defaults to 'present' + +group + Group to chgrp to. + +mode + Unix permissions, suitable for chmod. + +owner + User to chown to. + + +BOOLEAN PARAMETERS +------------------ +parents + Whether to create parents as well (mkdir -p behaviour). + Warning: all intermediate directory permissions default + to whatever mkdir -p does. + + Usually this means root:root, 0700. + +recursive + If supplied the chgrp and chown call will run recursively. + This does *not* influence the behaviour of chmod. + +MESSAGES +-------- +chgrp + Changed group membership +chown + Changed owner +chmod + Changed mode +create + Empty directory was created +remove + Directory exists, but state is absent, directory will be removed by generated code. +remove non directory + Something other than a directory with the same name exists and was removed prior to create. + + +EXAMPLES +-------- + +.. code-block:: sh + + # A silly example + __install_directory /tmp/foobar + + # Remove a directory + __install_directory /tmp/foobar --state absent + + # Ensure /etc exists correctly + __install_directory /etc --owner root --group root --mode 0755 + + # Create nfs service directory, including parents + __install_directory /home/services/nfs --parents + + # Change permissions recursively + __install_directory /home/services --recursive --owner root --group root + + # Setup a temp directory + __install_directory /local --mode 1777 + + # Take it all + __install_directory /home/services/kvm --recursive --parents \ + --owner root --group root --mode 0755 --state present + + +AUTHORS +------- +Nico Schottelius + + +COPYING +------- +Copyright \(C) 2011 Nico Schottelius. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__install_file/man.rst b/cdist/conf/type/__install_file/man.rst index c5409167..977ed77c 100644 --- a/cdist/conf/type/__install_file/man.rst +++ b/cdist/conf/type/__install_file/man.rst @@ -23,6 +23,10 @@ symlink directory replace it with the source file +One exception is that when state is pre-exists, an error is raised if +the file would have been created otherwise (e.g. it is not present or +not a regular file). + In any case, make sure that the file attributes are as specified. @@ -33,7 +37,7 @@ None. OPTIONAL PARAMETERS ------------------- state - 'present', 'absent' or 'exists', defaults to 'present' where: + 'present', 'absent', 'exists' or 'pre-exists', defaults to 'present' where: present the file is exactly the one from source @@ -41,6 +45,9 @@ state the file does not exist exists the file from source but only if it doesn't already exist + pre-exists + check that the file exists and is a regular file, but do not + create or modify it group Group to chgrp to. @@ -56,6 +63,9 @@ source If not supplied, an empty file or directory will be created. If source is '-' (dash), take what was written to stdin as the file content. +onchange + The code to run if file is modified. + MESSAGES -------- chgrp @@ -93,6 +103,8 @@ EXAMPLES __install_file /home/frodo/.bashrc --source "/etc/skel/.bashrc" \ --state exists \ --owner frodo --mode 0600 + # Check that the file is present, show an error when it is not + __install_file /etc/somefile --state pre-exists # Take file content from stdin __install_file /tmp/whatever --owner root --group root --mode 644 --source - << DONE Here goes the content for /tmp/whatever diff --git a/cdist/conf/type/__install_generate_fstab/gencode-local b/cdist/conf/type/__install_generate_fstab/gencode-local index 5cc7d877..80455aaa 100755 --- a/cdist/conf/type/__install_generate_fstab/gencode-local +++ b/cdist/conf/type/__install_generate_fstab/gencode-local @@ -23,12 +23,13 @@ cat "$__type/files/fstab.header" > "$destination" mkdir "$__object/files" # get current UUID's from target_host -$__remote_exec $__target_host blkid > "$__object/files/blkid" +$__remote_exec "$__target_host" blkid > "$__object/files/blkid" -for object in $(find "$__global/object/__install_mount" -type d -name "$__cdist_object_marker"); do +find "$__global/object/__install_mount" -type d -name "$__cdist_object_marker" | +while IFS= read -r object +do device="$(cat "$object/parameter/device")" dir="$(cat "$object/parameter/dir")" - prefix="$(cat "$object/parameter/prefix")" type="$(cat "$object/parameter/type")" if [ -f "$object/parameter/options" ]; then options="$(cat "$object/parameter/options")" @@ -54,7 +55,7 @@ for object in $(find "$__global/object/__install_mount" -type d -name "$__cdist_ ;; esac if [ -f "$__object/parameter/uuid" ]; then - uuid="$(grep -w $device "$__object/files/blkid" | awk '{print $2}')" + uuid="$(grep -w "$device" "$__object/files/blkid" | awk '{print $2}')" if [ -n "$uuid" ]; then echo "# $dir was on $device during installation" >> "$destination" device="$uuid" diff --git a/cdist/conf/type/__install_mkfs/manifest b/cdist/conf/type/__install_mkfs/manifest index eb65757f..b0a21dae 100755 --- a/cdist/conf/type/__install_mkfs/manifest +++ b/cdist/conf/type/__install_mkfs/manifest @@ -19,13 +19,7 @@ # # set defaults -if [ -f "$__object/parameter/device" ]; then - device="(cat "$__object/parameter/device")" -else +if [ ! -f "$__object/parameter/device" ]; then device="/$__object_id" echo "$device" > "$__object/parameter/device" fi - -type="(cat "$__object/parameter/type")" - -options="(cat "$__object/parameter/options")" diff --git a/cdist/conf/type/__install_mount/gencode-remote b/cdist/conf/type/__install_mount/gencode-remote index ce96279a..4415f0ff 100755 --- a/cdist/conf/type/__install_mount/gencode-remote +++ b/cdist/conf/type/__install_mount/gencode-remote @@ -20,7 +20,9 @@ get_type_from_mkfs() { _device="$1" - for mkfs_object in $(find "$__global/object/__install_mkfs" -type d -name "$__cdist_object_marker"); do + find "$__global/object/__install_mkfs" -type d -name "$__cdist_object_marker" | + while IFS= read -r mkfs_object + do mkfs_device="$(cat "$mkfs_object/parameter/device")" if [ "$_device" = "$mkfs_device" ]; then cat "$mkfs_object/parameter/type" diff --git a/cdist/conf/type/__install_partition_msdos_apply/files/lib.sh b/cdist/conf/type/__install_partition_msdos_apply/files/lib.sh index cddc575d..2db9a441 100644 --- a/cdist/conf/type/__install_partition_msdos_apply/files/lib.sh +++ b/cdist/conf/type/__install_partition_msdos_apply/files/lib.sh @@ -1,18 +1,20 @@ +#!/bin/sh + die() { - echo "[__install_partition_msdos_apply] $@" >&2 + echo "[__install_partition_msdos_apply] $*" >&2 exit 1 } debug() { - #echo "[__install_partition_msdos_apply] $@" >&2 + #echo "[__install_partition_msdos_apply] $*" >&2 : } fdisk_command() { - local device="$1" - local cmd="$2" + device="$1" + cmd="$2" debug fdisk_command "running fdisk command '${cmd}' on device ${device}" - printf "${cmd}\nw\n" | fdisk -c -u "$device" + printf '%s\nw\n' "${cmd}" | fdisk -c -u "$device" ret=$? # give disk some time sleep 1 @@ -20,49 +22,49 @@ fdisk_command() { } create_disklabel() { - local device=$1 + device=$1 debug create_disklabel "creating new msdos disklabel" - fdisk_command ${device} "o" + fdisk_command "${device}" "o" return $? } toggle_bootable() { - local device="$1" - local minor="$2" - fdisk_command ${device} "a\n${minor}\n" + device="$1" + minor="$2" + fdisk_command "${device}" "a\\n${minor}\\n" return $? } create_partition() { - local device="$1" - local minor="$2" - local size="$3" - local type="$4" - local primary_count="$5" + device="$1" + minor="$2" + size="$3" + type="$4" + primary_count="$5" - if [ "$type" = "extended" -o "$type" = "5" ]; then + if [ "$type" = "extended" ] || [ "$type" = "5" ]; then # Extended partition - primary_extended="e\n" - first_minor="${minor}\n" + primary_extended='e\n' + first_minor="${minor}\\n" [ "${minor}" = "4" ] && first_minor="" - type_minor="${minor}\n" + type_minor="${minor}\\n" [ "${minor}" = "1" ] && type_minor="" type="5" elif [ "${minor}" -lt "5" ]; then - primary_extended="p\n" - first_minor="${minor}\n" + primary_extended='p\n' + first_minor="${minor}\\n" [ "${minor}" = "4" ] && first_minor="" - type_minor="${minor}\n" + type_minor="${minor}\\n" [ "${minor}" = "1" ] && type_minor="" else # Logical partitions - first_minor="${minor}\n" - type_minor="${minor}\n" - primary_extended="l\n" + first_minor="${minor}\\n" + type_minor="${minor}\\n" + primary_extended='l\n' [ "$primary_count" -gt "3" ] && primary_extended="" fi [ -n "${size}" ] && size="+${size}M" - fdisk_command ${device} "n\n${primary_extended}${first_minor}\n${size}\nt\n${type_minor}${type}\n" + fdisk_command "${device}" "n\\n${primary_extended}${first_minor}\\n${size}\\nt\\n${type_minor}${type}\\n" return $? } diff --git a/cdist/conf/type/__install_partition_msdos_apply/gencode-remote b/cdist/conf/type/__install_partition_msdos_apply/gencode-remote index 090a5d86..a0b46b2d 100755 --- a/cdist/conf/type/__install_partition_msdos_apply/gencode-remote +++ b/cdist/conf/type/__install_partition_msdos_apply/gencode-remote @@ -21,35 +21,35 @@ #set -x die() { - echo "[__install_partition_msdos_apply] $@" >&2 + echo "[__install_partition_msdos_apply] $*" >&2 exit 1 } debug() { - #echo "[__install_partition_msdos_apply] $@" >&2 + #echo "[__install_partition_msdos_apply] $*" >&2 : } # Convert a size specifier 1G 100M or 50% into the corresponding numeric MB. size_to_mb() { - local size=$1 - local available_size="$2" + size=$1 + available_size="$2" - local number_suffix="$(echo ${size} | sed -e 's:\.[0-9]\+::' -e 's:\([0-9]\+\)\([KkMmGg%]\)[Bb]\?:\1|\2:')" - local number="$(echo ${number_suffix} | cut -d '|' -f1)" - local suffix="$(echo ${number_suffix} | cut -d '|' -f2)" + number_suffix="$(echo "${size}" | sed -e 's:\.[0-9]\+::' -e 's:\([0-9]\+\)\([KkMmGg%]\)[Bb]\?:\1|\2:')" + number="$(echo "${number_suffix}" | cut -d '|' -f1)" + suffix="$(echo "${number_suffix}" | cut -d '|' -f2)" case "$suffix" in K|k) - size="$(( $number / 1024 ))" + size="$(( number / 1024 ))" ;; M|m) size="$number" ;; G|g) - size="$(( $number * 1024 ))" + size="$(( number * 1024 ))" ;; %) - size="$(( $available_size * $number / 100 ))" + size="$(( available_size * number / 100 ))" ;; *) size="-1" @@ -59,13 +59,15 @@ size_to_mb() { get_objects() { objects_file=$(mktemp) - for object in $(find "$__global/object/__install_partition_msdos" -type d -name "$__cdist_object_marker"); do + find "$__global/object/__install_partition_msdos" -type d -name "$__cdist_object_marker" | + while IFS= read -r object + do object_device="$(cat "$object/parameter/device")" object_minor="$(cat "$object/parameter/minor")" - echo "$object_device $object_minor $object" >> $objects_file + echo "$object_device $object_minor $object" >> "$objects_file" done - sort -k 1,2 $objects_file | cut -d' ' -f 3 - rm $objects_file + sort -k 1,2 "$objects_file" | cut -d' ' -f 3 + rm "$objects_file" unset objects_file unset object unset object_device @@ -85,9 +87,9 @@ primary_count=0 for object in $objects; do device="$(cat "$object/parameter/device")" if [ "$current_device" != "$device" ]; then - echo "create_disklabel \"$device\" || die 'Failed to create disklabel for $device'" + echo "create_disklabel '$device' || die 'Failed to create disklabel for $device'" current_device="$device" - device_name=$(echo ${device} | sed -e 's:^/dev/::;s:/:\\/:g') + device_name=$(echo "${device}" | sed -e 's:^/dev/::;s:/:\\/:g') available_device_size=$(( $(awk "/${device_name}\$/ { print \$3; }" "$partitions") / 1024)) # make sure we don't go past the end of the drive available_device_size=$((available_device_size - 2)) @@ -108,7 +110,7 @@ for object in $objects; do if [ "${minor}" -lt "5" ]; then # Primary partitions - primary_count=$(( $primary_count + 1 )) + primary_count=$(( primary_count + 1 )) available_size=$available_device_size else # Logical partitions @@ -121,13 +123,13 @@ for object in $objects; do available_size=0 else partition_size=$(size_to_mb "$size" "$available_size") - available_size="$(( $available_size - $partition_size ))" + available_size="$(( available_size - partition_size ))" fi if [ "${minor}" -lt "5" ]; then # Primary partitions available_device_size=$available_size - if [ "$type" = "extended" -o "$type" = "5" ]; then + if [ "$type" = "extended" ] || [ "$type" = "5" ]; then # Extended partition available_extended_size=$partition_size fi diff --git a/cdist/conf/type/__install_reboot/gencode-remote b/cdist/conf/type/__install_reboot/gencode-remote index 00c04523..9a6322c1 100755 --- a/cdist/conf/type/__install_reboot/gencode-remote +++ b/cdist/conf/type/__install_reboot/gencode-remote @@ -18,8 +18,6 @@ # along with cdist. If not, see . # -options="$(cat "$__object/parameter/options")" - #echo "reboot $options" cat << DONE echo 1 > /proc/sys/kernel/sysrq diff --git a/cdist/conf/type/__install_reboot/man.rst b/cdist/conf/type/__install_reboot/man.rst index ecf78ca7..9a53b37a 100644 --- a/cdist/conf/type/__install_reboot/man.rst +++ b/cdist/conf/type/__install_reboot/man.rst @@ -18,8 +18,7 @@ None OPTIONAL PARAMETERS ------------------- -options - options to pass to the reboot command. e.g. -f +None EXAMPLES diff --git a/cdist/conf/type/__install_reset_disk/gencode-remote b/cdist/conf/type/__install_reset_disk/gencode-remote index 947dd472..ac9ae6cf 100755 --- a/cdist/conf/type/__install_reset_disk/gencode-remote +++ b/cdist/conf/type/__install_reset_disk/gencode-remote @@ -67,5 +67,5 @@ fi # erase partition table dd if=/dev/zero of=$disk bs=512 count=1 -printf 'w\n' | fdisk -u -c $disk || true +printf 'w\\n' | fdisk -u -c $disk || true DONE diff --git a/cdist/conf/type/__install_umount/parameter/default/target b/cdist/conf/type/__install_umount/parameter/default/target new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/cdist/conf/type/__install_umount/parameter/default/target @@ -0,0 +1 @@ +/target diff --git a/cdist/conf/type/__install_umount/parameter/optional b/cdist/conf/type/__install_umount/parameter/optional new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/cdist/conf/type/__install_umount/parameter/optional @@ -0,0 +1 @@ +target diff --git a/cdist/conf/type/__iptables_apply/files/init-script b/cdist/conf/type/__iptables_apply/files/init-script index 2247dcf5..e42017ae 100644 --- a/cdist/conf/type/__iptables_apply/files/init-script +++ b/cdist/conf/type/__iptables_apply/files/init-script @@ -1,7 +1,4 @@ #!/bin/sh -# Nico Schottelius -# Zürisee, Mon Sep 2 18:38:27 CEST 2013 -# ### BEGIN INIT INFO # Provides: iptables # Required-Start: $local_fs $remote_fs @@ -14,32 +11,72 @@ # and saves/restores previous status ### END INIT INFO +# Originally written by: +# Nico Schottelius +# Zürisee, Mon Sep 2 18:38:27 CEST 2013 +# +# 2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2020 Matthias Stecher (matthiasstecher at gmx.de) +# +# This file is distributed with cdist and licenced under the +# GNU GPLv3+ WITHOUT ANY WARRANTY. + + +# Read files and execute the content with the given commands +# +# Arguments: +# 1: Directory +# 2..n: Commands which should be used to execute the file content +gothrough() { + cd "$1" || return + shift + + # iterate through all rules and continue if it's not a file + for rule in *; do + [ -f "$rule" ] || continue + echo "Appling iptables rule $rule ..." + + # execute it with all commands specificed + ruleparam="$(cat "$rule")" + for cmd in "$@"; do + # Command and Rule should be split. + # shellcheck disable=SC2046 + command $cmd $ruleparam + done + done +} + +# Shortcut for iptables command to do IPv4 and v6 +# only applies to the "reset" target +iptables() { + command iptables "$@" + command ip6tables "$@" +} basedir=/etc/iptables.d -status="${basedir}/.pre-start" +status4="${basedir}/.pre-start" +status6="${basedir}/.pre-start6" case $1 in start) # Save status - iptables-save > "$status" + iptables-save > "$status4" + ip6tables-save > "$status6" # Apply our ruleset - cd "$basedir" - count="$(ls -1 | wc -l)" - - # Only do something if there are rules - if [ "$count" -ge 1 ]; then - for rule in *; do - echo "Applying iptables rule $rule ..." - iptables $(cat "$rule") - done - fi + gothrough "$basedir" iptables + #gothrough "$basedir/v4" iptables # conflicts with $basedir + gothrough "$basedir/v6" ip6tables + gothrough "$basedir/all" iptables ip6tables ;; stop) # Restore from status before, if there is something to restore - if [ -f "$status" ]; then - iptables-restore < "$status" + if [ -f "$status4" ]; then + iptables-restore < "$status4" + fi + if [ -f "$status6" ]; then + ip6tables-restore < "$status6" fi ;; restart) diff --git a/cdist/conf/type/__iptables_apply/man.rst b/cdist/conf/type/__iptables_apply/man.rst index 76e1f6bf..3bef92cc 100644 --- a/cdist/conf/type/__iptables_apply/man.rst +++ b/cdist/conf/type/__iptables_apply/man.rst @@ -10,7 +10,24 @@ DESCRIPTION ----------- This cdist type deploys an init script that triggers the configured rules and also re-applies them on -configuration. +configuration. Rules are written from __iptables_rule +into the folder ``/etc/iptables.d/``. + +It reads all rules from the base folder as rules for IPv4. +Rules in the subfolder ``v6/`` are IPv6 rules. Rules in +the subfolder ``all/`` are applied to both rule tables. All +files contain the arguments for a single ``iptables`` and/or +``ip6tables`` command. + +Rules are applied in the following order: +1. All IPv4 rules +2. All IPv6 rules +2. All rules that should be applied to both tables + +The order of the rules that will be applied are definite +from the result the shell glob returns, which should be +alphabetical. If rules must be applied in a special order, +prefix them with a number like ``02-some-rule``. REQUIRED PARAMETERS @@ -24,7 +41,7 @@ None EXAMPLES -------- -None (__iptables_apply is used by __iptables_rule) +None (__iptables_apply is used by __iptables_rule automatically) SEE ALSO @@ -35,11 +52,13 @@ SEE ALSO AUTHORS ------- Nico Schottelius +Matthias Stecher COPYING ------- -Copyright \(C) 2013 Nico Schottelius. You can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. +Copyright \(C) 2013 Nico Schottelius. +Copyright \(C) 2020 Matthias Stecher. +You can redistribute it and/or modify it under the terms of the GNU +General Public License as published by the Free Software Foundation, +either version 3 of the License, or (at your option) any later version. diff --git a/cdist/conf/type/__iptables_rule/man.rst b/cdist/conf/type/__iptables_rule/man.rst index 92d8859f..afb71e01 100644 --- a/cdist/conf/type/__iptables_rule/man.rst +++ b/cdist/conf/type/__iptables_rule/man.rst @@ -11,6 +11,10 @@ DESCRIPTION This cdist type allows you to manage iptable rules in a distribution independent manner. +See :strong:`cdist-type__iptables_apply`\ (7) for the +execution order of these rules. It will be executed +automaticly to apply all rules non-volaite. + REQUIRED PARAMETERS ------------------- @@ -25,6 +29,24 @@ state 'present' or 'absent', defaults to 'present' +BOOLEAN PARAMETERS +------------------ +All rules without any of these parameters will be treated like ``--v4`` because +of backward compatibility. + +v4 + Explicitly set it as rule for IPv4. If IPv6 is set, too, it will be + threaten like ``--all``. Will be the default if nothing else is set. + +v6 + Explicitly set it as rule for IPv6. If IPv4 is set, too, it will be + threaten like ``--all``. + +all + Set the rule for both IPv4 and IPv6. It will be saved separately from the + other rules. + + EXAMPLES -------- @@ -48,6 +70,16 @@ EXAMPLES --state absent + # IPv4-only rule for ICMPv4 + __iptables_rule icmp-v4 --v4 --rule "-A INPUT -p icmp -j ACCEPT" + # IPv6-only rule for ICMPv6 + __iptables_rule icmp-v6 --v6 --rule "-A INPUT -p icmpv6 -j ACCEPT" + + # doing something for the dual stack + __iptables_rule fwd-eth0-eth1 --v4 --v6 --rule "-A INPUT -i eth0 -o eth1 -j ACCEPT" + __iptables_rule fwd-eth1-eth0 --all --rule "-A -o eth1 -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT" + + SEE ALSO -------- :strong:`cdist-type__iptables_apply`\ (7), :strong:`iptables`\ (8) @@ -56,11 +88,13 @@ SEE ALSO AUTHORS ------- Nico Schottelius +Matthias Stecher COPYING ------- -Copyright \(C) 2013 Nico Schottelius. You can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. +Copyright \(C) 2013 Nico Schottelius. +Copyright \(C) 2020 Matthias Stecher. +You can redistribute it and/or modify it under the terms of the GNU +General Public License as published by the Free Software Foundation, +either version 3 of the License, or (at your option) any later version. diff --git a/cdist/conf/type/__iptables_rule/manifest b/cdist/conf/type/__iptables_rule/manifest index ed78787f..d4394c25 100755 --- a/cdist/conf/type/__iptables_rule/manifest +++ b/cdist/conf/type/__iptables_rule/manifest @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2020 Matthias Stecher (matthiasstecher at gmx.de) # # This file is part of cdist. # @@ -24,12 +25,36 @@ base_dir=/etc/iptables.d name="$__object_id" state="$(cat "$__object/parameter/state")" +if [ -f "$__object/parameter/v4" ]; then + only_v4="yes" + # $specific_dir is $base_dir +fi +if [ -f "$__object/parameter/v6" ]; then + only_v6="yes" + specific_dir="$base_dir/v6" +fi +# If rules should be set for both protocols +if { [ "$only_v4" = "yes" ] && [ "$only_v6" = "yes" ]; } || + [ -f "$__object/parameter/all" ]; then + + # all to a specific directory + specific_dir="$base_dir/all" +fi + +# set rule directory based on if it's the base or subdirectory +rule_dir="${specific_dir:-$base_dir}" + ################################################################################ # Basic setup # __directory "$base_dir" --state present +# sub-directory if required +if [ "$specific_dir" ]; then + require="__directory/$base_dir" __directory "$specific_dir" --state present +fi + # Have apply do the real job require="$__object_name" __iptables_apply @@ -37,6 +62,15 @@ require="$__object_name" __iptables_apply # The rule # -require="__directory/$base_dir" __file "$base_dir/${name}" \ - --source "$__object/parameter/rule" \ - --state "$state" +for dir in "$base_dir" "$base_dir/v6" "$base_dir/all"; do + # defaults to absent except the directory that should contain the file + if [ "$rule_dir" = "$dir" ]; then + curr_state="$state" + else + curr_state="absent" + fi + + require="__directory/$rule_dir" __file "$dir/$name" \ + --source "$__object/parameter/rule" \ + --state "$curr_state" +done diff --git a/cdist/conf/type/__iptables_rule/parameter/boolean b/cdist/conf/type/__iptables_rule/parameter/boolean new file mode 100644 index 00000000..76882272 --- /dev/null +++ b/cdist/conf/type/__iptables_rule/parameter/boolean @@ -0,0 +1,3 @@ +all +v4 +v6 diff --git a/cdist/conf/type/__jail/manifest b/cdist/conf/type/__jail/manifest index c3d9dfbe..fad6a3a1 100755 --- a/cdist/conf/type/__jail/manifest +++ b/cdist/conf/type/__jail/manifest @@ -35,16 +35,15 @@ fi jaildir="$(cat "$__object/parameter/jaildir")" -__directory ${jaildir} --parents +__directory "${jaildir}" --parents -set -- "$@" "$__object_id" "--state" "$state" +set -- "$@" "$__object_id" cd "$__object/parameter" -for property in $(ls .); do +for property in *; do set -- "$@" "--$property" "$(cat "$property")" done -ver="$(cat "$__global/explorer/os_version")" -if [ -n "$(echo "$ver" | grep '^10\.' )" ]; then # Version is 10.x +if grep -q '^10\.' "$(cat "$__global/explorer/os_version")" ; then # Version is 10.x __jail_freebsd10 "$@" else __jail_freebsd9 "$@" diff --git a/cdist/conf/type/__jail_freebsd10/explorer/status b/cdist/conf/type/__jail_freebsd10/explorer/status index 1ceba212..c8039f21 100755 --- a/cdist/conf/type/__jail_freebsd10/explorer/status +++ b/cdist/conf/type/__jail_freebsd10/explorer/status @@ -39,7 +39,7 @@ fi # backslash-escaped $jaildir sjaildir="$(echo ${jaildir} | sed 's#/#\\/#g')" -jls_output="$(jls | grep "[ ]${sjaildir}\/${name}\$")" || true +jls_output="$(jls | grep "[ ]${sjaildir}\\/${name}\$")" || true if [ -n "${jls_output}" ]; then echo "STARTED" diff --git a/cdist/conf/type/__jail_freebsd10/gencode-local b/cdist/conf/type/__jail_freebsd10/gencode-local index b2016f86..f163cad3 100755 --- a/cdist/conf/type/__jail_freebsd10/gencode-local +++ b/cdist/conf/type/__jail_freebsd10/gencode-local @@ -44,7 +44,7 @@ basepresent="$(cat "$__object/explorer/basepresent")" if [ "$state" = "present" ]; then if [ "$basepresent" = "NONE" ]; then # IPv6 fix - if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$') + if echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$' then my_target_host="[${__target_host}]" else diff --git a/cdist/conf/type/__jail_freebsd10/gencode-remote b/cdist/conf/type/__jail_freebsd10/gencode-remote index 76241e0e..4f376c25 100755 --- a/cdist/conf/type/__jail_freebsd10/gencode-remote +++ b/cdist/conf/type/__jail_freebsd10/gencode-remote @@ -36,7 +36,7 @@ state="$(cat "$__object/parameter/state")" started="true" # If the user wants the jail gone, it implies it shouldn't be started. -[ -f "$__object/parameter/stopped" -o "$state" = "absent" ] && started="false" +{ [ -f "$__object/parameter/stopped" ] || [ "$state" = "absent" ]; } && started="false" if [ -f "$__object/parameter/ip" ]; then ip="$(cat "$__object/parameter/ip")" @@ -45,7 +45,7 @@ else # when $state=present, it's required. Enforce this. if [ "$state" = "present" ]; then exec >&2 - echo "If --state is 'present,' --ip must be given\!" + printf 'If --state is "present", --ip must be given\!\n' exit 1 fi fi @@ -66,7 +66,7 @@ devfsruleset="$(cat "$__object/parameter/devfs-ruleset")" # devfs_ruleset being defined without devfs_enable being true # is pointless. Treat this as an error. -if [ -n "$devfsruleset" -a "$devfsenable" = "false" ]; then +if [ -n "$devfsruleset" ] && [ "$devfsenable" = "false" ]; then exec >&2 echo "Can't have --devfs-ruleset defined with --devfs-disable" exit 1 @@ -83,12 +83,12 @@ present="$(cat "$__object/explorer/present")" status="$(cat "$__object/explorer/status")" # Handle ip="addr, addr" format -if [ $(expr "${ip}" : ".*, .*") -gt "0" ]; then +if [ "$(expr "${ip}" : ".*, .*")" -gt "0" ]; then SAVE_IFS="$IFS" IFS=", " for cur_ip in ${ip}; do # Just get the last IP address for SSH to listen on - mgmt_ip=$(echo "${ip}" | cut '-d ' -f1) # In case using "ip netmask" format rather than CIDR + mgmt_ip=$(echo "${cur_ip}" | cut '-d ' -f1) # In case using "ip netmask" format rather than CIDR done IFS="$SAVE_IFS" else @@ -114,19 +114,19 @@ startJail() { deleteJail() { # Unmount the jail's mountpoints if necessary cat <=1 rw mount is mounted still - for DIR in "${output}"; do - umount -F "/etc/fstab.${name}" "\$(echo "${DIR}" | awk '{print $3}')" + for DIR in "\${output}"; do + umount -F "/etc/fstab.${name}" "\$(echo "${DIR}" | awk '{print \$3}')" done fi - output="\$(mount | grep "\/${name} (")" || true + output="\$(mount | grep "\\/${name} (")" || true if [ -n "\${output}" ]; then # ro mount is mounted still - umount -F "/etc/fstab.${name}" "\$(echo "${output}" | awk '{print $3}')" + umount -F "/etc/fstab.${name}" "\$(echo "\${output}" | awk '{print \$3}')" fi EOF # Remove the jail's rw mountpoints @@ -275,9 +275,9 @@ cat <&1 >/dev/null # Close the FD==fail... @@ -290,7 +290,7 @@ add include \\\$devfsrules_unhide_basic add include \\\$devfsrules_unhide_login END fi - devfsruleset_num=\$(grep "\[${devfsruleset}=" /etc/devfs.rules | sed -n 's/\[.*=\([0-9]*\)\]/\1/pg') + devfsruleset_num=\$(grep "\\[${devfsruleset}=" /etc/devfs.rules | sed -n 's/\\[.*=\\([0-9]*\\)\\]/\\1/pg') if [ -n "\$devfsruleset_num" ]; then jaildata="\$jaildata devfs_ruleset=\"\${devfsruleset_num}\";" @@ -298,8 +298,8 @@ END fi EOF - - echo "printf \"%s\\n%s\n%s\n\" \"\$jailheader\" \"\$jaildata\" \"\$jailtrailer\" >>\"\$jailfile\"" + # shellcheck disable=SC2028 + echo "printf \"%s\\n%s\\n%s\\n\" \"\$jailheader\" \"\$jaildata\" \"\$jailtrailer\" >>\"\$jailfile\"" # Add $name to jail_list if $onboot=yes if [ "$onboot" = "yes" ]; then diff --git a/cdist/conf/type/__jail_freebsd9/explorer/status b/cdist/conf/type/__jail_freebsd9/explorer/status index 1ceba212..c8039f21 100755 --- a/cdist/conf/type/__jail_freebsd9/explorer/status +++ b/cdist/conf/type/__jail_freebsd9/explorer/status @@ -39,7 +39,7 @@ fi # backslash-escaped $jaildir sjaildir="$(echo ${jaildir} | sed 's#/#\\/#g')" -jls_output="$(jls | grep "[ ]${sjaildir}\/${name}\$")" || true +jls_output="$(jls | grep "[ ]${sjaildir}\\/${name}\$")" || true if [ -n "${jls_output}" ]; then echo "STARTED" diff --git a/cdist/conf/type/__jail_freebsd9/gencode-local b/cdist/conf/type/__jail_freebsd9/gencode-local index 1ab7ff1a..bbdc9fcc 100755 --- a/cdist/conf/type/__jail_freebsd9/gencode-local +++ b/cdist/conf/type/__jail_freebsd9/gencode-local @@ -40,7 +40,7 @@ basepresent="$(cat "$__object/explorer/basepresent")" if [ "$state" = "present" ]; then if [ "$basepresent" = "NONE" ]; then # IPv6 fix - if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$') + if echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$' then my_target_host="[${__target_host}]" else diff --git a/cdist/conf/type/__jail_freebsd9/gencode-remote b/cdist/conf/type/__jail_freebsd9/gencode-remote index 63b48e5c..68229d3e 100755 --- a/cdist/conf/type/__jail_freebsd9/gencode-remote +++ b/cdist/conf/type/__jail_freebsd9/gencode-remote @@ -36,7 +36,7 @@ state="$(cat "$__object/parameter/state")" started="true" # If the user wants the jail gone, it implies it shouldn't be started. -[ -f "$__object/parameter/stopped" -o "$state" = "absent" ] && started="false" +{ [ -f "$__object/parameter/stopped" ] || [ "$state" = "absent" ]; } && started="false" if [ -f "$__object/parameter/ip" ]; then ip="$(cat "$__object/parameter/ip")" @@ -45,7 +45,7 @@ else # when $state=present, it's required. Enforce this. if [ "$state" = "present" ]; then exec >&2 - echo "If --state is 'present,' --ip must be given\!" + printf 'If --state is "present", --ip must be given\!\n' exit 1 fi fi @@ -70,7 +70,7 @@ devfsruleset="$(cat "$__object/parameter/devfs-ruleset")" # devfs_ruleset being defined without devfs_enable being true # is pointless. Treat this as an error. -if [ -n "$devfsruleset" -a "$devfsenable" = "false" ]; then +if [ -n "$devfsruleset" ] && [ "$devfsenable" = "false" ]; then exec >&2 echo "Can't have --devfs-ruleset defined with --devfs-disable" exit 1 @@ -86,14 +86,14 @@ present="$(cat "$__object/explorer/present")" status="$(cat "$__object/explorer/status")" # Handle ip="iface|addr, iface|addr" format -if [ $(expr "${ip}" : ".*|.*") -gt "0" ]; then +if [ "$(expr "${ip}" : ".*|.*")" -gt "0" ]; then # If we have multiple IPs defined, $interface doesn't make sense because ip="iface|addr, iface|addr" implies it interface="" SAVE_IFS="$IFS" IFS=", " for cur_ip in ${ip}; do # Just get the last IP address for SSH to listen on - mgmt_ip=$(echo "${ip}" | sed -E -e 's/^.*\|(.*)\/[0-9]+$/\1/') + mgmt_ip=$(echo "${cur_ip}" | sed -E -e 's/^.*\|(.*)\/[0-9]+$/\1/') done IFS="$SAVE_IFS" else @@ -119,19 +119,19 @@ startJail() { deleteJail() { # Unmount the jail's mountpoints if necessary cat <=1 rw mount is mounted still - for DIR in "${output}"; do - umount -F "/etc/fstab.${name}" "\$(echo "${DIR}" | awk '{print $3}')" + for DIR in "\${output}"; do + umount -F "/etc/fstab.${name}" "\$(echo "${DIR}" | awk '{print \$3}')" done fi - output="\$(mount | grep "\/${name} (")" || true + output="\$(mount | grep "\\/${name} (")" || true if [ -n "\${output}" ]; then # ro mount is mounted still - umount -F "/etc/fstab.${name}" "\$(echo "${output}" | awk '{print $3}')" + umount -F "/etc/fstab.${name}" "\$(echo "\${output}" | awk '{print \$3}')" fi EOF # Remove the jail's rw mountpoints @@ -279,9 +279,9 @@ END if [ ! -f /etc/devfs.rules ]; then touch /etc/devfs.rules fi - if [ -z "\$(grep '\[jailrules=' /etc/devfs.rules)" ]; then # The default ruleset doesn't exist + if [ -z "\$(grep '\\[jailrules=' /etc/devfs.rules)" ]; then # The default ruleset doesn't exist # Get the highest-numbered ruleset - highest="\$(sed -n 's/\[.*=\([0-9]*\)\]/\1/pg' /etc/devfs.rules | sort -u | tail -n 1)" || true + highest="\$(sed -n 's/\\[.*=\\([0-9]*\\)\\]/\\1/pg' /etc/devfs.rules | sort -u | tail -n 1)" || true # increment by 1 let num="\${highest}+1" 2>&- >&- # add default ruleset diff --git a/cdist/conf/type/__key_value/explorer/state b/cdist/conf/type/__key_value/explorer/state index b990733d..d24600af 100755 --- a/cdist/conf/type/__key_value/explorer/state +++ b/cdist/conf/type/__key_value/explorer/state @@ -19,9 +19,9 @@ # along with cdist. If not, see . # -export key="$(cat "$__object/parameter/key" 2>/dev/null \ +key="$(cat "$__object/parameter/key" 2>/dev/null \ || echo "$__object_id")" -export state="$(cat "$__object/parameter/state")" +state="$(cat "$__object/parameter/state")" file="$(cat "$__object/parameter/file")" @@ -30,16 +30,19 @@ if [ ! -f "$file" ]; then exit fi -export delimiter="$(cat "$__object/parameter/delimiter")" -export value="$(cat "$__object/parameter/value" 2>/dev/null \ +delimiter="$(cat "$__object/parameter/delimiter")" +value="$(cat "$__object/parameter/value" 2>/dev/null \ || echo "__CDIST_NOTSET__")" if [ -f "$__object/parameter/exact_delimiter" ]; then - export exact_delimiter=1 + exact_delimiter=1 else - export exact_delimiter=0 + exact_delimiter=0 fi +export key state delimiter value exact_delimiter -awk -f - "$file" <<"AWK_EOF" +awk_bin=$(PATH=$(getconf PATH 2>/dev/null) && command -v awk || echo awk) + +"${awk_bin}" -f - "$file" <<"AWK_EOF" BEGIN { state=ENVIRON["state"] key=ENVIRON["key"] diff --git a/cdist/conf/type/__key_value/files/remote_script.sh b/cdist/conf/type/__key_value/files/remote_script.sh index 52b3f2de..faf080cb 100644 --- a/cdist/conf/type/__key_value/files/remote_script.sh +++ b/cdist/conf/type/__key_value/files/remote_script.sh @@ -1,19 +1,21 @@ #!/bin/sh -export key="$(cat "$__object/parameter/key" 2>/dev/null \ +key="$(cat "$__object/parameter/key" 2>/dev/null \ || echo "$__object_id")" -export state="$(cat "$__object/parameter/state")" +state="$(cat "$__object/parameter/state")" file="$(cat "$__object/parameter/file")" -export delimiter="$(cat "$__object/parameter/delimiter")" -export value="$(cat "$__object/parameter/value" 2>/dev/null \ +delimiter="$(cat "$__object/parameter/delimiter")" +value="$(cat "$__object/parameter/value" 2>/dev/null \ || echo "__CDIST_NOTSET__")" +export key state delimiter value if [ -f "$__object/parameter/exact_delimiter" ]; then - export exact_delimiter=1 + exact_delimiter=1 else - export exact_delimiter=0 + exact_delimiter=0 fi +export exact_delimiter tmpfile=$(mktemp "${file}.cdist.XXXXXXXXXX") # preserve ownership and permissions by copying existing file over tmpfile @@ -22,7 +24,10 @@ if [ -f "$file" ]; then else touch "$file" fi -awk -f - "$file" >"$tmpfile" <<"AWK_EOF" + +awk_bin=$(PATH=$(getconf PATH 2>/dev/null) && command -v awk || echo awk) + +"${awk_bin}" -f - "$file" >"$tmpfile" <<"AWK_EOF" BEGIN { # import variables in a secure way .. state=ENVIRON["state"] diff --git a/cdist/conf/type/__key_value/gencode-remote b/cdist/conf/type/__key_value/gencode-remote index 13cc27c7..1174400e 100755 --- a/cdist/conf/type/__key_value/gencode-remote +++ b/cdist/conf/type/__key_value/gencode-remote @@ -25,7 +25,7 @@ state_should="$(cat "$__object/parameter/state")" state_is="$(cat "$__object/explorer/state")" fire_onchange='' -if [ "$state_is" = "$state_should" ]; then +if [ "$state_is" = "$state_should" ]; then exit 0 fi diff --git a/cdist/conf/type/__key_value/manifest b/cdist/conf/type/__key_value/manifest index c7801c89..5a91f60c 100755 --- a/cdist/conf/type/__key_value/manifest +++ b/cdist/conf/type/__key_value/manifest @@ -21,7 +21,7 @@ state_should="$(cat "$__object/parameter/state")" -if [ "$state_should" = "present" -a ! -f "$__object/parameter/value" ]; then +if [ "$state_should" = "present" ] && [ ! -f "$__object/parameter/value" ]; then echo "Missing required parameter 'value'" >&2 exit 1 fi diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/certbot-path b/cdist/conf/type/__letsencrypt_cert/explorer/certbot-path new file mode 100755 index 00000000..3c6076df --- /dev/null +++ b/cdist/conf/type/__letsencrypt_cert/explorer/certbot-path @@ -0,0 +1,3 @@ +#!/bin/sh -e + +command -v certbot 2>/dev/null || true diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/certificate-domains b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-domains new file mode 100755 index 00000000..db605b63 --- /dev/null +++ b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-domains @@ -0,0 +1,8 @@ +#!/bin/sh -e + +certbot_path=$("${__type_explorer}/certbot-path") +if [ -n "${certbot_path}" ] +then + certbot certificates --cert-name "${__object_id:?}" | grep ' Domains: ' | \ + cut -d ' ' -f 6- | tr ' ' '\n' +fi diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/certificate-exists b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-exists new file mode 100755 index 00000000..4e6f44db --- /dev/null +++ b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-exists @@ -0,0 +1,13 @@ +#!/bin/sh -e + +certbot_path=$("${__type_explorer}/certbot-path") +if [ -n "${certbot_path}" ] +then + if certbot certificates | grep -q " Certificate Name: ${__object_id:?}$"; then + echo yes + else + echo no + fi +else + echo no +fi diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/certificate-is-test b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-is-test new file mode 100755 index 00000000..9b445059 --- /dev/null +++ b/cdist/conf/type/__letsencrypt_cert/explorer/certificate-is-test @@ -0,0 +1,14 @@ +#!/bin/sh -e + +certbot_path=$("${__type_explorer}/certbot-path") +if [ -n "${certbot_path}" ] +then + if certbot certificates --cert-name "${__object_id:?}" | \ + grep -q 'INVALID: TEST_CERT'; then + echo yes + else + echo no + fi +else + echo no +fi diff --git a/cdist/conf/type/__letsencrypt_cert/explorer/exists b/cdist/conf/type/__letsencrypt_cert/explorer/exists deleted file mode 100644 index cb967663..00000000 --- a/cdist/conf/type/__letsencrypt_cert/explorer/exists +++ /dev/null @@ -1,5 +0,0 @@ -domain=$__object_id - -if [ -f "/etc/letsencrypt/live/$domain/fullchain.pem" ]; then - echo yes -fi diff --git a/cdist/conf/type/__letsencrypt_cert/gencode-remote b/cdist/conf/type/__letsencrypt_cert/gencode-remote old mode 100644 new mode 100755 index 62ada241..375570a4 --- a/cdist/conf/type/__letsencrypt_cert/gencode-remote +++ b/cdist/conf/type/__letsencrypt_cert/gencode-remote @@ -1,18 +1,82 @@ -domain="$__object_id" +#!/bin/sh -e -exists=$(cat "$__object/explorer/exists") -webroot="$(cat "$__object/parameter/webroot")" -admin_email="$(cat "$__object/parameter/admin-email")" +certificate_exists=$(cat "${__object:?}/explorer/certificate-exists") +name="${__object_id:?}" +state=$(cat "${__object}/parameter/state") -if [ -n "$exists" ]; then - exit 0 -fi +case "${state}" in + absent) + if [ "${certificate_exists}" = "no" ]; then + exit 0 + fi -cat <> "${__messages_out:?}" + ;; + present) + domain_param_file="${__object}/parameter/domain" + requested_domains=$(mktemp "${TMPDIR:-/tmp}/domain.cdist.XXXXXXXXXX") + if [ -f "${domain_param_file}" ]; then + cp "${domain_param_file}" "${requested_domains}" + else + echo "$__object_id" >> "${requested_domains}" + fi + + staging=no + if [ -f "${__object}/parameter/staging" ]; then + staging=yes + fi + + if [ "${certificate_exists}" = "yes" ]; then + existing_domains="${__object}/explorer/certificate-domains" + certificate_is_test=$(cat "${__object}/explorer/certificate-is-test") + + sort -uo "${requested_domains}" "${requested_domains}" + sort -uo "${existing_domains}" "${existing_domains}" + + if [ -z "$(comm -23 "${requested_domains}" "${existing_domains}")" ] && \ + [ "${certificate_is_test}" = "${staging}" ]; then + exit 0 + fi + fi + + admin_email="$(cat "$__object/parameter/admin-email")" + webroot="$(cat "$__object/parameter/webroot")" + + cat <<-EOF + certbot certonly \ + --agree-tos \ + --cert-name '${name}' \ + --email '${admin_email}' \ + --expand \ + --non-interactive \ + --quiet \ + $(if [ "${staging}" = "yes" ]; then + echo "--staging" + elif [ "${certificate_is_test}" != "${staging}" ]; then + echo "--force-renewal" + fi) \ + $(if [ -z "${webroot}" ]; then + echo "--standalone" + else + echo "--webroot --webroot-path '${webroot}'" + fi) \ + $(while read -r domain; do + echo "--domain '${domain}' \\" + done < "${requested_domains}") + EOF + rm -f "${requested_domains}" + + if [ "${certificate_exists}" = "no" ]; then + echo create >> "${__messages_out}" + else + echo change >> "${__messages_out}" + fi + ;; + *) + echo "Unsupported state: ${state}" >&2 + + exit 1 + ;; +esac diff --git a/cdist/conf/type/__letsencrypt_cert/man.rst b/cdist/conf/type/__letsencrypt_cert/man.rst index bb1e5d05..85eb88ea 100644 --- a/cdist/conf/type/__letsencrypt_cert/man.rst +++ b/cdist/conf/type/__letsencrypt_cert/man.rst @@ -3,54 +3,107 @@ cdist-type__letsencrypt_cert(7) NAME ---- -cdist-type__letsencrypt_cert - Get an SSL certificate from Let's Encrypt +cdist-type__letsencrypt_cert - Get an SSL certificate from Let's Encrypt DESCRIPTION ----------- -Automatically obtain a Let's Encrypt SSL certificate. Uses certbot's webroot -method. You must set up your web server to work with webroot. +Automatically obtain a Let's Encrypt SSL certificate using Certbot. REQUIRED PARAMETERS ------------------- -webroot - The path to your webroot, as set up in your webserver config. + +object id + A cert name. If domain parameter is not specified then it is used + as a domain to be included in the certificate. admin-email - Where to send Let's Encrypt emails like "certificate needs renewal". - + Where to send Let's Encrypt emails like "certificate needs renewal". OPTIONAL PARAMETERS ------------------- -None. +state + 'present' or 'absent', defaults to 'present' where: + + present + if the certificate does not exist, it will be obtained + absent + the certificate will be removed + +webroot + The path to your webroot, as set up in your webserver config. If this + parameter is not present, Certbot will be run in standalone mode. OPTIONAL MULTIPLE PARAMETERS ---------------------------- + renew-hook - Renew hook command directly passed to certbot in cron job. + Renew hook command directly passed to Certbot in cron job. + +domain + Domains to be included in the certificate. When specified then object id + is not used as a domain. + +BOOLEAN PARAMETERS +------------------ + +automatic-renewal + Install a cron job, which attempts to renew certificates daily. + +staging + Obtain a test certificate from a staging server. + +MESSAGES +-------- + +change + Certificate was changed. + +create + Certificate was created. + +remove + Certificate was removed. EXAMPLES -------- .. code-block:: sh - __letsencrypt_cert example.com --admin-email root@example.com --webroot /data/letsencrypt/root + # use object id as domain + __letsencrypt_cert example.com \ + --admin-email root@example.com \ + --automatic-renewal \ + --renew-hook "service nginx reload" \ + --webroot /data/letsencrypt/root - __letsencrypt_cert example.com --admin-email root@example.com --webroot /data/letsencrypt/root --renew-hook "service nginx reload" +.. code-block:: sh + # domain parameter is specified so object id is not used as domain + # and example.com needs to be included again with domain parameter + __letsencrypt_cert example.com \ + --admin-email root@example.com \ + --automatic-renewal \ + --domain example.com \ + --domain foo.example.com \ + --domain bar.example.com \ + --renew-hook "service nginx reload" \ + --webroot /data/letsencrypt/root AUTHORS ------- + | Nico Schottelius | Kamila Součková | Darko Poljak - +| Ľubomír Kučera COPYING ------- -Copyright \(C) 2017 Nico Schottelius, Kamila Součková, Darko Poljak. You can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. + +Copyright \(C) 2017-2018 Nico Schottelius, Kamila Součková, Darko Poljak and +Ľubomír Kučera. You can redistribute it and/or modify it under the terms of +the GNU General Public License as published by the Free Software Foundation, +either version 3 of the License, or (at your option) any later version. diff --git a/cdist/conf/type/__letsencrypt_cert/manifest b/cdist/conf/type/__letsencrypt_cert/manifest old mode 100644 new mode 100755 index 800e5e18..b4464366 --- a/cdist/conf/type/__letsencrypt_cert/manifest +++ b/cdist/conf/type/__letsencrypt_cert/manifest @@ -1,79 +1,118 @@ -os=$(cat "$__global/explorer/os") -os_version=$(cat "$__global/explorer/os_version") +#!/bin/sh -case "$os" in - debian) - case "$os_version" in - 8*) - __apt_source jessie-backports \ - --uri http://http.debian.net/debian \ - --distribution jessie-backports \ - --component main +certbot_fullpath="$(cat "${__object:?}/explorer/certbot-path")" - require="__apt_source/jessie-backports" __package_apt python-certbot --target-release jessie-backports - require="__apt_source/jessie-backports" __package_apt certbot --target-release jessie-backports - # Seems to be a missing dependency on debian 8 - __package python-ndg-httpsclient - ;; - 9*) - __apt_source stretch-backports \ - --uri http://http.debian.net/debian \ - --distribution stretch-backports \ - --component main +if [ -z "${certbot_fullpath}" ]; then + os="$(cat "${__global:?}/explorer/os")" + os_version="$(cat "${__global}/explorer/os_version")" - require="__apt_source/stretch-backports" __package_apt python-certbot --target-release stretch-backports - require="__apt_source/stretch-backports" __package_apt certbot --target-release stretch-backports - ;; - *) - echo "Unsupported OS version: $os_version" >&2 - exit 1 - ;; - esac + case "$os" in + archlinux) + __package certbot + ;; + alpine) + __package certbot + ;; + debian) + case "$os_version" in + 8*) + __apt_source jessie-backports \ + --uri http://http.debian.net/debian \ + --distribution jessie-backports \ + --component main - certbot_fullpath=/usr/bin/certbot - ;; - devuan) - case "$os_version" in - jessie) - __apt_source jessie-backports \ - --uri http://auto.mirror.devuan.org/merged \ - --distribution jessie-backports \ - --component main + require="__apt_source/jessie-backports" __package_apt python-certbot \ + --target-release jessie-backports + require="__apt_source/jessie-backports" __package_apt certbot \ + --target-release jessie-backports + # Seems to be a missing dependency on debian 8 + __package python-ndg-httpsclient + ;; + 9*) + __apt_source stretch-backports \ + --uri http://http.debian.net/debian \ + --distribution stretch-backports \ + --component main - require="__apt_source/jessie-backports" __package_apt python-certbot --target-release jessie-backports - require="__apt_source/jessie-backports" __package_apt certbot --target-release jessie-backports - # Seems to be a missing dependency on debian 8 - __package python-ndg-httpsclient - ;; - *) - echo "Unsupported OS version: $os_version" >&2 - exit 1 - ;; - esac + require="__apt_source/stretch-backports" __package_apt python-certbot \ + --target-release stretch-backports + require="__apt_source/stretch-backports" __package_apt certbot \ + --target-release stretch-backports + ;; + 10*) + __package_apt certbot + ;; - certbot_fullpath=/usr/bin/certbot - ;; - freebsd) - __package py27-certbot + *) + echo "Unsupported OS version: $os_version" >&2 + exit 1 + ;; + esac - certbot_fullpath=/usr/local/bin/certbot - ;; - *) - echo "Unsupported os: $os" >&2 - exit 1 - ;; -esac + certbot_fullpath=/usr/bin/certbot + ;; + devuan) + case "$os_version" in + jessie) + __apt_source jessie-backports \ + --uri http://auto.mirror.devuan.org/merged \ + --distribution jessie-backports \ + --component main -renew_hook_param="$__object/parameter/renew-hook" -renew_hook="" -if [ -f "$renew_hook_param" ]; then - while read hook; do - renew_hook="$renew_hook --renew-hook \"$hook\"" - done < "$renew_hook_param" + require="__apt_source/jessie-backports" __package_apt python-certbot \ + --target-release jessie-backports + require="__apt_source/jessie-backports" __package_apt certbot \ + --target-release jessie-backports + # Seems to be a missing dependency on debian 8 + __package python-ndg-httpsclient + ;; + ascii*) + __apt_source ascii-backports \ + --uri http://auto.mirror.devuan.org/merged \ + --distribution ascii-backports \ + --component main + + require="__apt_source/ascii-backports" __package_apt certbot \ + --target-release ascii-backports + ;; + beowulf*) + __package_apt certbot + ;; + *) + echo "Unsupported OS version: $os_version" >&2 + exit 1 + ;; + esac + + certbot_fullpath=/usr/bin/certbot + ;; + freebsd) + __package py27-certbot + + certbot_fullpath=/usr/local/bin/certbot + ;; + ubuntu) + __package certbot + ;; + *) + echo "Unsupported os: $os" >&2 + exit 1 + ;; + esac fi -__cron letsencrypt-certbot \ - --user root \ - --command "$certbot_fullpath renew -q $renew_hook" \ - --hour 0 \ - --minute 47 +if [ -f "${__object}/parameter/automatic-renewal" ]; then + renew_hook_param="${__object}/parameter/renew-hook" + renew_hook="" + if [ -f "${renew_hook_param}" ]; then + while read -r hook; do + renew_hook="${renew_hook} --renew-hook \"${hook}\"" + done < "${renew_hook_param}" + fi + + __cron letsencrypt-certbot \ + --user root \ + --command "${certbot_fullpath} renew -q ${renew_hook}" \ + --hour 0 \ + --minute 47 +fi diff --git a/cdist/conf/type/__letsencrypt_cert/parameter/boolean b/cdist/conf/type/__letsencrypt_cert/parameter/boolean new file mode 100644 index 00000000..d5b8be99 --- /dev/null +++ b/cdist/conf/type/__letsencrypt_cert/parameter/boolean @@ -0,0 +1,2 @@ +automatic-renewal +staging diff --git a/cdist/conf/type/__letsencrypt_cert/parameter/default/state b/cdist/conf/type/__letsencrypt_cert/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__letsencrypt_cert/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__letsencrypt_cert/parameter/default/webroot b/cdist/conf/type/__letsencrypt_cert/parameter/default/webroot new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__letsencrypt_cert/parameter/optional b/cdist/conf/type/__letsencrypt_cert/parameter/optional new file mode 100644 index 00000000..0a63b11e --- /dev/null +++ b/cdist/conf/type/__letsencrypt_cert/parameter/optional @@ -0,0 +1,2 @@ +state +webroot diff --git a/cdist/conf/type/__letsencrypt_cert/parameter/optional_multiple b/cdist/conf/type/__letsencrypt_cert/parameter/optional_multiple index 3384c74f..0e866d45 100644 --- a/cdist/conf/type/__letsencrypt_cert/parameter/optional_multiple +++ b/cdist/conf/type/__letsencrypt_cert/parameter/optional_multiple @@ -1 +1,2 @@ +domain renew-hook diff --git a/cdist/conf/type/__letsencrypt_cert/parameter/required b/cdist/conf/type/__letsencrypt_cert/parameter/required index 45fe4ea6..bfe77226 100644 --- a/cdist/conf/type/__letsencrypt_cert/parameter/required +++ b/cdist/conf/type/__letsencrypt_cert/parameter/required @@ -1,2 +1 @@ admin-email -webroot diff --git a/cdist/conf/type/__line/explorer/state b/cdist/conf/type/__line/explorer/state index 08056c86..9d480b19 100755 --- a/cdist/conf/type/__line/explorer/state +++ b/cdist/conf/type/__line/explorer/state @@ -1,6 +1,7 @@ -#!/bin/sh +#!/bin/sh -e # -# 2012-2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2018 Steven Armstrong (steven-cdist at armstrong.cc) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -17,26 +18,90 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -file="/$__object_id" -[ -f "$__object/parameter/file" ] && file=$(cat "$__object/parameter/file") +if [ -f "$__object/parameter/file" ]; then + file=$(cat "$__object/parameter/file") +else + file="/$__object_id" +fi + +[ -f "$file" ] || exit 0 + +if [ -f "$__object/parameter/before" ]; then + position="before" +elif [ -f "$__object/parameter/after" ]; then + position="after" +else + # By default we append to the end of the file. + position="end" +fi if [ -f "$__object/parameter/regex" ]; then - regex=$(cat "$__object/parameter/regex") - greparg="" + needle="regex" else - if [ ! -f "$__object/parameter/line" ]; then - echo "Parameter line and regex missing - cannot explore" >&2 - exit 1 - fi - regex="$(cat "$__object/parameter/line")" - greparg="-F -x" + needle="line" fi -# Allow missing file - thus 2>/dev/null -if grep -q $greparg -- "$regex" "$file" 2>/dev/null; then - echo present -else - echo absent -fi +awk -v position="$position" -v needle="$needle" ' +function _find(_text, _pattern) { + if (needle == "regex") { + return match(_text, _pattern) + } else { + return index(_text, _pattern) == 1 + } +} +BEGIN { + getline anchor < (ENVIRON["__object"] "/parameter/" position) + getline pattern < (ENVIRON["__object"] "/parameter/" needle) + getline line < (ENVIRON["__object"] "/parameter/line") + + found_line = 0 + correct_line = 0 + correct_pos = (position != "after" && position != "before") +} +{ + if (position == "after") { + if (match($0, anchor)) { + getline + if (_find($0, pattern)) { + found_line++ + if (index($0, line) == 1) { correct_line++ } + correct_pos = 1 + exit 0 + } + } else if (_find($0, pattern)) { + found_line++ + if (index($0, line) == 1) { correct_line++ } + } + } else if (position == "before") { + if (_find($0, pattern)) { + found_line++ + if (index($0, line) == 1) { correct_line++ } + getline + if (match($0, anchor)) { + correct_pos = 1 + exit 0 + } + } + } else { + if (_find($0, pattern)) { + found_line++ + if (index($0, line) == 1) { correct_line++ } + exit 0 + } + } +} +END { + if (found_line && correct_pos) { + if (correct_line) { + print "present" + } else { + print "matching" + } + } else if (found_line) { + print "wrongposition" + } else { + print "absent" + } +} +' "$file" diff --git a/cdist/conf/type/__line/gencode-remote b/cdist/conf/type/__line/gencode-remote index 4a75b4c5..a89886da 100755 --- a/cdist/conf/type/__line/gencode-remote +++ b/cdist/conf/type/__line/gencode-remote @@ -1,7 +1,7 @@ #!/bin/sh -e # -# 2012 Nico Schottelius (nico-cdist at schottelius.org) -# 2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2018 Steven Armstrong (steven-cdist at armstrong.cc) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -18,76 +18,123 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -file="/$__object_id" -regex="" -state_should="present" -[ -f "$__object/parameter/file" ] && file=$(cat "$__object/parameter/file") -[ -f "$__object/parameter/regex" ] && regex=$(cat "$__object/parameter/regex") -[ -f "$__object/parameter/state" ] && state_should=$(cat "$__object/parameter/state") -[ -f "$__object/parameter/line" ] && line=$(cat "$__object/parameter/line") +if [ -f "$__object/parameter/before" ] && [ -f "$__object/parameter/after" ]; then + echo "Use either --before OR --after but not both." >&2 + exit 1 +fi +if [ -f "$__object/parameter/file" ]; then + file="$(cat "$__object/parameter/file")" +else + file="/$__object_id" +fi + +state_should="$(cat "$__object/parameter/state")" state_is="$(cat "$__object/explorer/state")" -[ "$state_should" = "$state_is" ] && exit 0 +if [ -z "$state_is" ]; then + printf 'The file "%s" is missing. Please create it before using %s on it.\n' "$file" "${__type##*/}" >&2 + exit 1 +fi +if [ "$state_should" = "$state_is" ] || \ + { [ "$state_should" = "present" ] && [ "$state_is" = "matching" ] ;} || \ + { [ "$state_should" = "replace" ] && [ "$state_is" = "present" ] ;} ; then + # If state matches already, or 'present' is used and regex matches + # or 'replace' is used and the exact line is present, then there is + # nothing to do + exit 0 +fi + +if [ -f "$__object/parameter/before" ]; then + position="before" +elif [ -f "$__object/parameter/after" ]; then + position="after" +else + # By default we append to the end of the file. + position="end" +fi + +if [ -f "$__object/parameter/regex" ]; then + needle="regex" +else + needle="line" +fi + +add=0 +remove=0 case "$state_should" in - present) - if [ ! "$line" ]; then - echo "Required parameter \"line\" is missing" >&2 - exit 1 - fi + present|replace) + if [ "$state_is" = "wrongposition" ] || [ "$state_is" = "matching" ]; then + echo updated >> "$__messages_out" + remove=1 + else + echo added >> "$__messages_out" + fi + add=1 + ;; + absent) + echo removed >> "$__messages_out" + remove=1 + ;; +esac - #echo "echo \"$line\" >> $file" - #line_sanitised=$(cat "$__object/parameter/line" | sed 's/"/\"/g') - # Idea: replace ' in the string: - # '"'"' - # |------> ': end the string - # |-|---> "'": create ' in the output string - # |--> ': continue the string - # - # Replace all \ so \t and other combinations are not interpreted - # - - - # line_sanitised=$(cat "$__object/parameter/line" | sed -e "s/'/'\"'\"'/g" -e 's/\\/\\\\/g') - # The one above does not work: - # --line "PS1='[\t] \[\033[1m\]\h\[\033[0m\]:\w\\$ '" - # becomes - # PS1='[\\t] \\[\\033[1m\\]\\h\\[\\033[0m\\]:\\w\\$ ' - - # Only replace ' with '"'"' and keep \ as they are - line_sanitised=$(cat "$__object/parameter/line" | sed -e "s/'/'\"'\"'/g") - printf '%s' "printf '%s\n' '$line_sanitised' >> $file" - echo "added" >> "$__messages_out" - - ;; - absent) - if [ "$regex" -a "$line" ]; then - echo "Mutally exclusive parameters regex and line given for state absent" >&2 - exit 1 - fi - - greparg="" - if [ "$line" ]; then - regex="$line" - greparg="-F -x" - fi - - cat << eof +cat << DONE tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX) # preserve ownership and permissions of existing file if [ -f "$file" ]; then cp -p "$file" "\$tmpfile" fi -grep -v $greparg "$regex" '$file' > \$tmpfile || true + +awk -v position="$position" -v needle="$needle" -v remove=$remove -v add=$add ' +function _find(_text, _pattern) { + if (needle == "regex") { + return match(_text, _pattern) + } else { + return index(_text, _pattern) + } +} +BEGIN { + line_file = ENVIRON["__object"] "/parameter/line" + getline line < line_file + # Need to close line file as it may be re-read as pattern below. + close(line_file) + getline pattern < (ENVIRON["__object"] "/parameter/" needle) + getline anchor < (ENVIRON["__object"] "/parameter/" position) +} +{ + if (remove) { + if (_find(\$0, pattern)) { + # skip over this line -> remove it + next + } + } + if (add) { + if (anchor && match(\$0, anchor)) { + if (position == "before") { + print line + add = 0 + print + } else if (position == "after") { + print + print line + add = 0 + } + next + } + } + print +} +END { + if (add) { + print line + } +} +' "$file" > "\$tmpfile" mv -f "\$tmpfile" "$file" -eof - echo "removed" >> "$__messages_out" - ;; - *) - echo "Unknown state: $state_should" >&2 - exit 1 - ;; -esac +DONE + +if [ -f "$__object/parameter/onchange" ]; then + cat "$__object/parameter/onchange" +fi diff --git a/cdist/conf/type/__line/man.rst b/cdist/conf/type/__line/man.rst index b63ea2b3..70490f68 100644 --- a/cdist/conf/type/__line/man.rst +++ b/cdist/conf/type/__line/man.rst @@ -13,72 +13,113 @@ This cdist type allows you to add lines and remove lines from files. REQUIRED PARAMETERS ------------------- +None. + OPTIONAL PARAMETERS ------------------- -state - 'present' or 'absent', defaults to 'present' +after + Insert the given line after this pattern. -line - Specifies the line which should be absent or present - - Must be present, if state is present. - Must not be combined with regex, if state is absent. - -regex - If state is present, search for this pattern and add - given line, if the given regular expression does not match. - - In case of absent, ensure all lines matching the - regular expression are absent. - - The regular expression is interpreted by grep. - - Must not be combined with line, if state is absent. +before + Insert the given line before this pattern. file If supplied, use this as the destination file. Otherwise the object_id is used. +line + Specifies the line which should be absent or present. + + Must be present, if state is 'present' or 'replace'. + Ignored if regex is given and state is 'absent'. + +regex + If state is 'present', search for this pattern and if it matches add + the given line. + + If state is 'absent', ensure all lines matching the regular expression + are absent. + + If state is 'replace', ensure all lines matching the regular expression + are exactly 'line'. + + The regular expression is interpreted by awk's match function. + +state + 'present', 'absent' or 'replace', defaults to 'present'. + +onchange + The code to run if line is added, removed or updated. + + +BOOLEAN PARAMETERS +------------------ +None. + + MESSAGES -------- added - The line was added. + The line was added. + +updated + The line or its position was changed. removed - The line was removed. + The line was removed. + EXAMPLES -------- .. code-block:: sh - # Manage the DAEMONS line in rc.conf - __line daemons --file /etc/rc.conf --line 'DAEMONS=(hwclock !network sshd crond postfix)' + # Manage a hosts entry for www.example.com. + __line /etc/hosts \ + --line '127.0.0.2 www.example.com' - # Ensure the home mount is present in /etc/fstab - explicitly make it present - __line home-fstab \ - --file /etc/fstab \ - --line 'filer.fs:/vol/home /home nfs defaults 0 0' \ - --state present + # Manage another hosts entry for test.example.com. + __line hosts:test.example.com \ + --file /etc/hosts \ + --line '127.0.0.3 test.example.com' - # Removes the line specifiend in "include_www" from the file "lighttpd.conf" - __line legacy_timezone --file /etc/rc.conf --regex 'TIMEZONE=.*' --state absent + # Remove the line starting with TIMEZONE from the /etc/rc.conf file. + __line legacy_timezone \ + --file /etc/rc.conf \ + --regex 'TIMEZONE=.*' \ + --state absent + + # Insert a line before another one. + __line password-auth-local:classify \ + --file /etc/pam.d/password-auth-local \ + --line '-session required pam_exec.so debug log=/tmp/classify.log /usr/local/libexec/classify' \ + --before '^session[[:space:]]+include[[:space:]]+password-auth-ac$' + + # Insert a line after another one. + __line password-auth-local:classify \ + --file /etc/pam.d/password-auth-local \ + --line '-session required pam_exec.so debug log=/tmp/classify.log /usr/local/libexec/classify' \ + --after '^session[[:space:]]+include[[:space:]]+password-auth-ac$' + + # Uncomment as needed and set a value in a configuration file. + __line /etc/example.conf \ + --line 'SomeSetting SomeValue' \ + --regex '^(#[[:space:]]*)?SomeSetting[[:space:]]' \ + --state replace SEE ALSO -------- -:strong:`grep`\ (1) +:strong:`cdist-type`\ (7) AUTHORS ------- -Nico Schottelius +Steven Armstrong COPYING ------- -Copyright \(C) 2012-2013 Nico Schottelius. You can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. +Copyright \(C) 2018 Steven Armstrong. Free use of this software is +granted under the terms of the GNU General Public License version 3 (GPLv3). diff --git a/cdist/conf/type/__line/parameter/default/state b/cdist/conf/type/__line/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__line/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__line/parameter/optional b/cdist/conf/type/__line/parameter/optional index 604a203e..1c34c699 100644 --- a/cdist/conf/type/__line/parameter/optional +++ b/cdist/conf/type/__line/parameter/optional @@ -1,4 +1,7 @@ -state -regex +after +before file line +regex +state +onchange diff --git a/cdist/conf/type/__link/explorer/state b/cdist/conf/type/__link/explorer/state index b8d8fc2b..7150df25 100755 --- a/cdist/conf/type/__link/explorer/state +++ b/cdist/conf/type/__link/explorer/state @@ -32,9 +32,9 @@ destination_dir="${destination%/*}" case "$type" in symbolic) - cd "$destination_dir" - source_is=$(ls -l "$destination" | sed 's/.*-> //g') + cd "$destination_dir" || exit 1 if [ -h "$destination" ]; then + source_is=$(readlink "$destination") # ignore trailing slashes for comparison if [ "${source_is%/}" = "${source%/}" ]; then echo present @@ -46,13 +46,19 @@ case "$type" in fi ;; hard) - cd "$destination_dir" + cd "$destination_dir" || exit 1 # check source relative to destination_dir if [ ! -e "$source" ]; then echo sourcemissing exit 0 fi + # Currently not worth the effor to change it, stat is not defined by POSIX + # and different OSes has different implementations for it. + # shellcheck disable=SC2012 destination_inode=$(ls -i "$destination" | awk '{print $1}') + # Currently not worth the effor to change it, stat is not defined by POSIX + # and different OSes has different implementations for it. + # shellcheck disable=SC2012 source_inode=$(ls -i "$source" | awk '{print $1}') if [ "$destination_inode" -eq "$source_inode" ]; then echo present diff --git a/cdist/conf/type/__link/explorer/type b/cdist/conf/type/__link/explorer/type index 579fd081..b322bf42 100755 --- a/cdist/conf/type/__link/explorer/type +++ b/cdist/conf/type/__link/explorer/type @@ -24,23 +24,26 @@ destination="/$__object_id" if [ ! -e "$destination" ]; then - echo none + echo none elif [ -h "$destination" ]; then - echo symlink + echo symlink elif [ -f "$destination" ]; then - type="$(cat "$__object/parameter/type")" - case "$type" in - hard) - link_count=$(ls -l "$destination" | awk '{ print $2 }') - if [ $link_count -gt 1 ]; then - echo hardlink - exit 0 - fi - ;; - esac - echo file + type="$(cat "$__object/parameter/type")" + case "$type" in + hard) + # Currently not worth the effor to change it, stat is not defined by POSIX + # and different OSes has different implementations for it. + # shellcheck disable=SC2012 + link_count=$(ls -l "$destination" | awk '{ print $2 }') + if [ "$link_count" -gt 1 ]; then + echo hardlink + exit 0 + fi + ;; + esac + echo file elif [ -d "$destination" ]; then - echo directory + echo directory else - echo unknown + echo unknown fi diff --git a/cdist/conf/type/__link/gencode-remote b/cdist/conf/type/__link/gencode-remote index 4467fb8e..45c22fcc 100755 --- a/cdist/conf/type/__link/gencode-remote +++ b/cdist/conf/type/__link/gencode-remote @@ -48,21 +48,25 @@ case "$state_should" in if [ "$file_type" = "directory" ]; then # our destination is currently a directory, delete it printf 'rm -rf "%s" &&\n' "$destination" + echo "removed '$destination' (directory)" >> "$__messages_out" else if [ "$state_is" = "wrongsource" ]; then # our destination is a symlink but points to the wrong source, # delete it printf 'rm -f "%s" &&\n' "$destination" + echo "removed '$destination' (wrongsource)" >> "$__messages_out" fi fi # create our link printf 'ln %s -f "%s" "%s"\n' "$lnopt" "$source" "$destination" + echo "created '$destination'" >> "$__messages_out" ;; absent) # only delete if it is a sym/hard link - if [ "$file_type" = "symlink" -o "$file_type" = "hardlink" ]; then + if [ "$file_type" = "symlink" ] || [ "$file_type" = "hardlink" ]; then printf 'rm -f "%s"\n' "$destination" + echo "removed '$destination'" >> "$__messages_out" fi ;; *) diff --git a/cdist/conf/type/__link/man.rst b/cdist/conf/type/__link/man.rst index 9dc4665f..2e81aea9 100644 --- a/cdist/conf/type/__link/man.rst +++ b/cdist/conf/type/__link/man.rst @@ -18,7 +18,7 @@ source Specifies the link source. type - Specifies the link type: Either hard or symoblic. + Specifies the link type: Either hard or symbolic. OPTIONAL PARAMETERS @@ -27,6 +27,22 @@ state 'present' or 'absent', defaults to 'present' +MESSAGES +-------- + +created + Link to destination was created. + +removed + Link to destination was removed. + +removed (directory) + Destination was removed because state is ``present`` and destination was directory. + +removed (wrongsource) + Destination was removed because state is ``present`` and destination link source was wrong. + + EXAMPLES -------- diff --git a/cdist/conf/type/__locale/deprecated b/cdist/conf/type/__locale/deprecated new file mode 100644 index 00000000..5a06b28e --- /dev/null +++ b/cdist/conf/type/__locale/deprecated @@ -0,0 +1 @@ +This type is deprecated. Please use __localedef instead. diff --git a/cdist/conf/type/__locale/explorer/state b/cdist/conf/type/__locale/explorer/state new file mode 100755 index 00000000..4494fcbc --- /dev/null +++ b/cdist/conf/type/__locale/explorer/state @@ -0,0 +1,36 @@ +#!/bin/sh -e +# __locale/explorer/state +# +# 2020 Matthias Stecher (matthiasstecher at gmx.de) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Check if the locale is already installed on the system. +# Outputs 'present' or 'absent' depending if the locale exists. +# + + +# Get user-defined locale +# locale name is echoed differently than the user propably set it (for UTF-8) +locale="$(echo "$__object_id" | sed 's/UTF-8/utf8/')" + +# Check if the given locale exists on the system +if localedef --list-archive | grep -qFx "$locale"; then + echo present +else + echo absent +fi diff --git a/cdist/conf/type/__locale/gencode-remote b/cdist/conf/type/__locale/gencode-remote index 04e48712..4639cef8 100755 --- a/cdist/conf/type/__locale/gencode-remote +++ b/cdist/conf/type/__locale/gencode-remote @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # @@ -23,6 +23,15 @@ locale="$__object_id" +state_is=$(cat "$__object/explorer/state") +state_should=$(cat "$__object/parameter/state") + +# short circuit if there is nothing to do +if [ "$state_is" = "$state_should" ]; then + exit 0 +fi + + # Hardcoded, create a pull request with # branching on $os in case it is at another location alias=/usr/share/locale/locale.alias @@ -35,9 +44,16 @@ charmap=$(echo "$locale" | cut -d . -f 2) # W-T-F! locale_remove=$(echo "$locale" | sed 's/UTF-8/utf8/') -state=$(cat "$__object/parameter/state") +os=$(cat "$__global/explorer/os") -case "$state" in +# Nothing to be done on alpine +case "$os" in + alpine) + exit 0 + ;; +esac + +case "$state_should" in present) echo localedef -A "$alias" -f "$charmap" -i "$input" "$locale" ;; @@ -45,7 +61,7 @@ case "$state" in echo localedef --delete-from-archive "$locale_remove" ;; *) - echo "Unsupported state: $state" >&2 + echo "Unsupported state: $state_should" >&2 exit 1 ;; esac diff --git a/cdist/conf/type/__locale/man.rst b/cdist/conf/type/__locale/man.rst index 60a4eacc..e36ab061 100644 --- a/cdist/conf/type/__locale/man.rst +++ b/cdist/conf/type/__locale/man.rst @@ -8,7 +8,8 @@ cdist-type__locale - Configure locales DESCRIPTION ----------- -This cdist type allows you to setup locales. +This cdist type allows you to setup locales. On systems that don't +support locale setting like alpine/musl libc, it is a no-op. OPTIONAL PARAMETERS @@ -44,6 +45,6 @@ Nico Schottelius COPYING ------- -Copyright \(C) 2013-2016 Nico Schottelius. Free use of this software is +Copyright \(C) 2013-2019 Nico Schottelius. Free use of this software is granted under the terms of the GNU General Public License version 3 or later (GPLv3+). diff --git a/cdist/conf/type/__locale/manifest b/cdist/conf/type/__locale/manifest index cacd0b42..9f1e17ac 100755 --- a/cdist/conf/type/__locale/manifest +++ b/cdist/conf/type/__locale/manifest @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2013-2015 Nico Schottelius (nico-cdist at schottelius.org) +# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org) # 2015 David Hürlimann (david at ungleich.ch) # # This file is part of cdist. @@ -19,7 +19,7 @@ # along with cdist. If not, see . # # -# Install required packages +# Install required packages # os=$(cat "$__global/explorer/os") @@ -30,7 +30,7 @@ case "$os" in # Debian needs a seperate package __package locales --state present ;; - archlinux|suse|ubuntu|scientific|centos) + archlinux|suse|ubuntu|scientific|centos|alpine) : ;; *) diff --git a/cdist/conf/type/__locale_system/manifest b/cdist/conf/type/__locale_system/manifest index 80f7401b..4b996ebc 100755 --- a/cdist/conf/type/__locale_system/manifest +++ b/cdist/conf/type/__locale_system/manifest @@ -3,6 +3,7 @@ # 2012-2016 Steven Armstrong (steven-cdist at armstrong.cc) # 2016 Carlos Ortigoza (carlos.ortigoza at ungleich.ch) # 2016 Nico Schottelius (nico.schottelius at ungleich.ch) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -23,17 +24,171 @@ # Configure system-wide locale by modifying i18n file. # +version_ge() { + awk -F '[^0-9.]' -v target="${1:?}" ' + function max(x, y) { return x > y ? x : y } + BEGIN { + getline + nx = split($1, x, ".") + ny = split(target, y, ".") + for (i = 1; i <= max(nx, ny); ++i) { + diff = int(x[i]) - int(y[i]) + if (diff == 0) continue + exit (diff < 0) + } + }' +} + + +key=$__object_id +onchange_cmd= # none, by default +quote_value=false + +catval() { + # shellcheck disable=SC2059 + printf "$($quote_value && echo '"%s"' || echo '%s')" "$(cat "$1")" +} + +state_should=$(cat "${__object}/parameter/state") + os=$(cat "$__global/explorer/os") -case "$os" in - debian|ubuntu) +case $os +in + debian) + if version_ge 4 <"${__global}/explorer/os_version" + then + # Debian 4 (etch) and later + locale_conf="/etc/default/locale" + else + locale_conf="/etc/environment" + fi + ;; + devuan) locale_conf="/etc/default/locale" ;; + ubuntu) + if version_ge 6.10 <"${__global}/explorer/os_version" + then + # Ubuntu 6.10 (edgy) and later + locale_conf="/etc/default/locale" + else + locale_conf="/etc/environment" + fi + ;; archlinux) locale_conf="/etc/locale.conf" ;; - redhat|centos) - locale_conf="/etc/sysconfig/i18n" + centos|redhat|scientific) + # shellcheck source=/dev/null + version_id=$(. "${__global}/explorer/os_release" && echo "${VERSION_ID:-0}") + if echo "${version_id}" | version_ge 7 + then + locale_conf="/etc/locale.conf" + else + locale_conf="/etc/sysconfig/i18n" + fi + ;; + fedora) + # shellcheck source=/dev/null + version_id=$(. "${__global}/explorer/os_release" && echo "${VERSION_ID:-0}") + if echo "${version_id}" | version_ge 18 + then + locale_conf="/etc/locale.conf" + quote_value=false + else + locale_conf="/etc/sysconfig/i18n" + fi + ;; + gentoo) + case $(cat "${__global}/explorer/init") + in + (*openrc*) + locale_conf="/etc/env.d/02locale" + onchange_cmd="env-update --no-ldconfig" + quote_value=true + ;; + (systemd) + locale_conf="/etc/locale.conf" + ;; + esac + ;; + freebsd|netbsd) + # NetBSD doesn't have a separate configuration file to set locales. + # In FreeBSD locales could be configured via /etc/login.conf but parsing + # that would be annoying, so the shell login file will have to do. + # "Non-POSIX" shells like csh will not be updated here. + + locale_conf="/etc/profile" + quote_value=true + value="$(catval "${__object}/parameter/value"); export ${key}" + ;; + solaris) + locale_conf="/etc/default/init" + locale_conf_group="sys" + + if version_ge 5.11 <"${__global}/explorer/os_version" + then + # mode on Oracle Solaris 11 is actually 0444, + # but the write bit makes sense, IMO + locale_conf_mode=0644 + + # Oracle Solaris 11.2 and later uses SMF to store environment info. + # This is a hack, but I didn't feel like modifying the whole type + # just for some Oracle nonsense. + # 11.3 apparently added nlsadm(1m), but it is missing from 11.2. + # Illumos continues to use /etc/default/init + # NOTE: Remember not to use "cool" POSIX features like -q or -e with + # Solaris grep. + release_regex='Oracle Solaris 11.[2-9][0-9]*' + case $state_should + in + (present) + svccfg_cmd="svccfg -s svc:/system/environment:init setprop environment/${key} = astring: '$(cat "${__object}/parameter/value")'" + ;; + (absent) + svccfg_cmd="svccfg -s svc:/system/environment:init delprop environment/${key}" + ;; + esac + refresh_cmd='svcadm refresh svc:/system/environment' + onchange_cmd="grep '${release_regex}' /etc/release >&- || exit 0; ${svccfg_cmd:-:} && ${refresh_cmd}" + else + locale_conf_mode=0555 + fi + ;; + slackware) + # NOTE: lang.csh (csh config) is ignored here. + locale_conf="/etc/profile.d/lang.sh" + locale_conf_mode=0755 + key="export ${__object_id}" + ;; + suse) + if test -s "${__global}/explorer/os_release" + then + # shellcheck source=/dev/null + os_version=$(. "${__global}/explorer/os_release" && echo "${VERSION}") + else + os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global}/explorer/os_version") + fi + os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)') + + # https://documentation.suse.com/sles/15-SP2/html/SLES-all/cha-suse.html#sec-suse-l10n + if expr "${os_major}" '>=' 15 \& "${os_major}" != 42 + then + # It seems that starting with SuSE 15 the systemd /etc/locale.conf + # is the preferred way to set locales, although + # /etc/sysconfig/language is still available. + # Older documentation doesn't mention /etc/locale.conf, even though + # is it created when localectl is used. + locale_conf="/etc/locale.conf" + else + locale_conf="/etc/sysconfig/language" + quote_value=true + key="RC_${__object_id}" + fi + ;; + voidlinux) + locale_conf="/etc/locale.conf" ;; *) echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2 @@ -42,14 +197,16 @@ case "$os" in ;; esac -__file "$locale_conf" \ - --owner root --group root --mode 644 \ - --state exists +__file "${locale_conf}" --state exists \ + --owner "${locale_conf_owner:-0}" \ + --group "${locale_conf_group:-0}" \ + --mode "${locale_conf_mode:-0644}" -require="__file/$locale_conf" \ - __key_value "$locale_conf:$__object_id" \ - --file "$locale_conf" \ - --key "$__object_id" \ - --delimiter = \ - --state "$(cat "$__object/parameter/state")" \ - --value "$(cat "$__object/parameter/value")" +require="__file/${locale_conf}" \ +__key_value "${locale_conf}:${key#export }" \ + --file "${locale_conf}" \ + --key "${key}" \ + --delimiter '=' --exact_delimiter \ + --state "${state_should}" \ + --value "${value:-$(catval "${__object}/parameter/value")}" \ + --onchange "${onchange_cmd}" diff --git a/cdist/conf/type/__localedef/explorer/state b/cdist/conf/type/__localedef/explorer/state new file mode 100755 index 00000000..3ba57661 --- /dev/null +++ b/cdist/conf/type/__localedef/explorer/state @@ -0,0 +1,100 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer determines if the locale is defined on the target system. +# Will print nothing on error. +# +# Possible output: +# present: +# the main locale (and possibly aliases) is present +# absent: +# neither the main locale nor any aliases are present +# alias-present: +# the main locale is absent, but at least one of its aliases is present +# + +# Hardcoded, create a pull request in case it is at another location for +# some other distro. (cf. gencode-remote) +aliasfile='/usr/share/locale/locale.alias' + +command -v locale >/dev/null 2>&1 || exit 0 + +locales=$(locale -a) + +parse_locale() { + # This function will split locales into their parts. Locale strings are + # usually of the form: [language[_territory][.codeset][@modifier]] + # For simplicity, language and territory are not separated by this function. + # Old Linux systems were also using "english" or "german" as locale strings. + # Usage: parse_locale locale_str lang_var codeset_var modifier_var + eval "${2:?}"="$(expr "$1" : '\([^.@]*\)')" + eval "${3:?}"="$(expr "$1" : '[^.]*\.\([^@]*\)')" + eval "${4:?}"="$(expr "$1" : '.*@\(.*\)$')" +} + +format_locale() { + # Usage: format_locale language codeset modifier + printf '%s' "$1" + test -z "$2" || printf '.%s' "$2" + test -z "$3" || printf '@%s' "$3" + printf '\n' +} + +gnu_normalize_codeset() { + # reimplementation of glibc/locale/programs/localedef.c normalize_codeset() + echo "$*" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]' +} + +locale_available() ( + echo "${locales}" | grep -qxF "$1" || { + # glibc uses "normalized" locale names in archives. + # If a locale is stored in an archive, the normalized name will be + # printed by locale, so that needs to be checked, too. + localename=$( + parse_locale "$1" _lang _codeset _modifier \ + && format_locale "${_lang:?}" "$(gnu_normalize_codeset "${_codeset?}")" \ + "${_modifier?}") + echo "${locales}" | grep -qxF "${localename}" + } +) + +if locale_available "${__object_id:?}" +then + echo present +else + # NOTE: locale.alias can be symlinked. + if test -e "${aliasfile}" + then + # Check if one of the aliases of the locale is defined + baselocale=$( + parse_locale "${__object_id:?}" _lang _codeset _modifiers \ + && format_locale "${_lang}" "${_codeset}") + while read -r _alias _localename + do + if test "${_localename}" = "${baselocale}" \ + && echo "${locales}" | grep -qxF "${_alias}" + then + echo alias-present + exit 0 + fi + done <"${aliasfile}" + fi + + echo absent +fi diff --git a/cdist/conf/type/__localedef/files/lib/glibc.sh b/cdist/conf/type/__localedef/files/lib/glibc.sh new file mode 100644 index 00000000..6ace80d4 --- /dev/null +++ b/cdist/conf/type/__localedef/files/lib/glibc.sh @@ -0,0 +1,5 @@ +# -*- mode: sh; indent-tabs-mode: t -*- + +gnu_normalize_codeset() { + echo "$*" | tr -cd '[:alnum:]' | tr '[:upper:]' '[:lower:]' +} diff --git a/cdist/conf/type/__localedef/files/lib/locale.sh b/cdist/conf/type/__localedef/files/lib/locale.sh new file mode 100644 index 00000000..b5e61374 --- /dev/null +++ b/cdist/conf/type/__localedef/files/lib/locale.sh @@ -0,0 +1,20 @@ +# -*- mode: sh; indent-tabs-mode:t -*- + +parse_locale() { + # This function will split locales into their parts. Locale strings are + # usually of the form: [language[_territory][.codeset][@modifier]] + # For simplicity, language and territory are not separated by this function. + # Old Linux systems were also using "english" or "german" as locale strings. + # Usage: parse_locale locale_str lang_var codeset_var modifier_var + eval "${2:?}"="$(expr "$1" : '\([^.@]*\)')" + eval "${3:?}"="$(expr "$1" : '[^.]*\.\([^@]*\)')" + eval "${4:?}"="$(expr "$1" : '.*@\(.*\)$')" +} + +format_locale() { + # Usage: format_locale language codeset modifier + printf '%s' "$1" + test -z "$2" || printf '.%s' "$2" + test -z "$3" || printf '@%s' "$3" + printf '\n' +} diff --git a/cdist/conf/type/__localedef/gencode-remote b/cdist/conf/type/__localedef/gencode-remote new file mode 100755 index 00000000..4538151f --- /dev/null +++ b/cdist/conf/type/__localedef/gencode-remote @@ -0,0 +1,136 @@ +#!/bin/sh -e +# +# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Manage system locales using localedef(1). +# + +# shellcheck source=cdist/conf/type/__localedef/files/lib/locale.sh +. "${__type:?}/files/lib/locale.sh" +# shellcheck source=cdist/conf/type/__localedef/files/lib/glibc.sh +. "${__type:?}/files/lib/glibc.sh" + +state_is=$(cat "${__object:?}/explorer/state") +state_should=$(cat "${__object:?}/parameter/state") + +test "${state_should}" = 'present' -o "${state_should}" = 'absent' || { + printf 'Invalid state: %s\n' "${state_should}" >&2 + exit 1 +} + +# NOTE: If state explorer fails (e.g. locale(1) missing), the following check +# will always fail and let definition/removal run. +if test "${state_is}" = "${state_should}" +then + exit 0 +fi + +locale=${__object_id:?} +os=$(cat "${__global:?}/explorer/os") + +if expr "${locale}" : '.*/' >/dev/null +then + printf 'Paths as locales are not supported.\n' >&2 + printf '__object_id is: %s\n' "${locale}" >&2 + exit 1 +fi + +: "${lang=}" "${codeset=}" "${modifier=}" # declare variables for shellcheck +parse_locale "${locale}" lang codeset modifier + + +case ${os} +in + (alpine|openwrt) + printf '%s does not support locales.\n' "${os}" >&2 + exit 1 + ;; + (archlinux|debian|devuan|ubuntu|suse|centos|fedora|redhat|scientific) + # FIXME: The code below only works for glibc-based installations. + + # NOTE: Hardcoded, create a pull request in case it is at another + # location for some opther distro. + # NOTE: locale.alias can be symlinked (e.g. Debian) + aliasfile='/usr/share/locale/locale.alias' + + case ${state_should} + in + (present) + input=$(format_locale "${lang}" '' "${modifier}") + cat <<-EOF + set -- + if test -e '${aliasfile}' + then + set -- -A '${aliasfile}' + fi + + localedef -i '${input}' -f '${codeset}' "\$@" '${locale}' + EOF + ;; + (absent) + main_localename=$(format_locale "${lang}" "$(gnu_normalize_codeset "${codeset}")" "${modifier}") + + cat <<-EOF + while read -r _alias _localename + do + if test "\${_localename}" = '$(format_locale "${lang}" "${codeset}")' + then + localedef --delete-from-archive "\${_alias}" + fi + done <'${aliasfile}' + EOF + + if test "${state_is}" = present + then + printf "localedef --delete-from-archive '%s'\n" "${main_localename}" + fi + ;; + esac + ;; + (freebsd) + case ${state_should} + in + (present) + if expr "$(grep -oe '^[0-9]*' "${__global:?}/explorer/os_version")" '>=' 11 >/dev/null + then + # localedef(1) is available with FreeBSD >= 11 + printf "localedef -i '%s' -f '%s' '%s'\n" "${input}" "${codeset}" "${locale}" + else + printf 'localedef(1) was added to FreeBSD starting with version 11.\n' >&2 + printf 'Please upgrade your FreeBSD installation to use %s.\n' "${__type##*/}" >&2 + exit 1 + fi + ;; + (absent) + printf "rm -R '/usr/share/locale/%s'\n" "${locale}" + ;; + esac + ;; + (netbsd|openbsd) + # NetBSD/OpenBSD are missing localedef(1). + # We also do not delete defined locales because they can't be recreated. + echo "${os} is lacking localedef(1). Locale management unavailable." >&2 + exit 1 + ;; + (*) + echo "Your operating system (${os}) is currently not supported by this type (${__type##*/})." >&2 + echo "Please contribute an implementation for it if you can." >&2 + exit 1 + ;; +esac diff --git a/cdist/conf/type/__localedef/man.rst b/cdist/conf/type/__localedef/man.rst new file mode 100644 index 00000000..454ce9d1 --- /dev/null +++ b/cdist/conf/type/__localedef/man.rst @@ -0,0 +1,60 @@ +cdist-type__localedef(7) +======================== + +NAME +---- +cdist-type__localedef - Define and remove system locales + + +DESCRIPTION +----------- +This cdist type allows you to define locales on the system using +:strong:`localedef`\ (1) or remove them. +On systems that don't support definition of new locales, the type will raise an +error. + +**NB:** This type respects the glibc ``locale.alias`` file, +i.e. it defines alias locales or deletes aliases of a locale when it is removed. +It is not possible, however, to use alias names to define locales or only remove +certain aliases of a locale. + + +OPTIONAL PARAMETERS +------------------- +state + ``present`` or ``absent``. Defaults to ``present``. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Add locale de_CH.UTF-8 + __localedef de_CH.UTF-8 + + # Same as above, but more explicit + __localedef de_CH.UTF-8 --state present + + # Remove colourful British English + __localedef en_GB.UTF-8 --state absent + + +SEE ALSO +-------- +:strong:`locale`\ (1), +:strong:`localedef`\ (1), +:strong:`cdist-type__locale_system`\ (7) + + +AUTHORS +------- +| Dennis Camera +| Nico Schottelius + + +COPYING +------- +Copyright \(C) 2013-2019 Nico Schottelius, 2020 Dennis Camera. Free use of this +software is granted under the terms of the GNU General Public License version 3 +or later (GPLv3+). diff --git a/cdist/conf/type/__localedef/manifest b/cdist/conf/type/__localedef/manifest new file mode 100755 index 00000000..3ab3ad8c --- /dev/null +++ b/cdist/conf/type/__localedef/manifest @@ -0,0 +1,30 @@ +#!/bin/sh -e +# +# 2013-2019 Nico Schottelius (nico-cdist at schottelius.org) +# 2015 David Hürlimann (david at ungleich.ch) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Install required packages. +# + +case $(cat "${__global:?}/explorer/os") +in + (debian|devuan) + __package_apt locales --state present + ;; +esac diff --git a/cdist/conf/type/__localedef/parameter/default/state b/cdist/conf/type/__localedef/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__localedef/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__localedef/parameter/optional b/cdist/conf/type/__localedef/parameter/optional new file mode 100644 index 00000000..ff72b5c7 --- /dev/null +++ b/cdist/conf/type/__localedef/parameter/optional @@ -0,0 +1 @@ +state diff --git a/cdist/conf/type/__motd/gencode-remote b/cdist/conf/type/__motd/gencode-remote index bc842cc8..cb7bfc84 100755 --- a/cdist/conf/type/__motd/gencode-remote +++ b/cdist/conf/type/__motd/gencode-remote @@ -22,14 +22,18 @@ os=$(cat "$__global/explorer/os") case "$os" in - debian|ubuntu|devuan) - - # Debian and Ubuntu need to be updated, - # as seen in /etc/init.d/bootlogs - echo "uname -snrvm > /var/run/motd" - echo "cat /etc/motd.tail >> /var/run/motd" + freebsd) + # FreeBSD only updates /etc/motd on boot, + # as seen in /etc/rc.d/motd + echo "uname -sri > /etc/motd" + echo "cat /etc/motd.template >> /etc/motd" + # FreeBSD 13 starts treating motd slightly different from previous + # versions this ensures hosts have the expected config. + echo "rm /etc/motd.template || true" + echo "service motd start" ;; *) + # Other OS tend to treat /etc/motd statically exit 0 ;; esac diff --git a/cdist/conf/type/__motd/man.rst b/cdist/conf/type/__motd/man.rst index 17369684..a567dc80 100644 --- a/cdist/conf/type/__motd/man.rst +++ b/cdist/conf/type/__motd/man.rst @@ -10,6 +10,13 @@ DESCRIPTION ----------- This cdist type allows you to easily setup /etc/motd. +.. note:: + In some OS, motd is a bit special, check `motd(5)`. + Currently Debian, Devuan, Ubuntu and FreeBSD are taken into account. + If your OS of choice does something besides /etc/motd, check the source + and contribute support for it. + Otherwise it will likely just work. + REQUIRED PARAMETERS ------------------- @@ -20,6 +27,7 @@ OPTIONAL PARAMETERS ------------------- source If supplied, copy this file from the host running cdist to the target. + If source is '-' (dash), take what was written to stdin as the file content. If not supplied, a default message will be placed onto the target. @@ -34,6 +42,15 @@ EXAMPLES # Supply source file from a different type __motd --source "$__type/files/my-motd" + # Supply source from stdin + __motd --source "-" < COPYING ------- -Copyright \(C) 2011 Nico Schottelius. You can redistribute it +Copyright \(C) 2020 Nico Schottelius. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/cdist/conf/type/__motd/manifest b/cdist/conf/type/__motd/manifest index cd741cf4..b8f74ebf 100755 --- a/cdist/conf/type/__motd/manifest +++ b/cdist/conf/type/__motd/manifest @@ -33,10 +33,14 @@ os=$(cat "$__global/explorer/os") case "$os" in - debian|ubuntu|devuan) - destination=/etc/motd.tail + freebsd) + # FreeBSD uses motd.template to prepend system information on boot + # (this actually only applies starting with version 13, + # but we fix that for whatever version in gencode-remote) + destination=/etc/motd.template ;; *) + # Most UNIX systems, including other Linux and OpenBSD just use /etc/motd destination=/etc/motd ;; esac diff --git a/cdist/conf/type/__mount/gencode-remote b/cdist/conf/type/__mount/gencode-remote index 66d85f88..b2096764 100755 --- a/cdist/conf/type/__mount/gencode-remote +++ b/cdist/conf/type/__mount/gencode-remote @@ -39,7 +39,7 @@ case "$state_should" in printf ' -o %s' "$(cat "$__object/parameter/options")" fi printf ' %s' "$(cat "$__object/parameter/device")" - printf " %s\n" "$path" + printf ' %s\n' "$path" else # mount using existing fstab entry printf 'mount "%s"\n' "$path" diff --git a/cdist/conf/type/__mount/manifest b/cdist/conf/type/__mount/manifest index 73937899..999d806c 100755 --- a/cdist/conf/type/__mount/manifest +++ b/cdist/conf/type/__mount/manifest @@ -31,7 +31,7 @@ printf " %s" "$type" options="$(cat "$__object/parameter/options")" printf " %s" "$options" printf " %s" "$(cat "$__object/parameter/dump")" -printf " %s\n" "$(cat "$__object/parameter/pass")" +printf ' %s\n' "$(cat "$__object/parameter/pass")" ) | \ __block "$__object_name" \ --file "/etc/fstab" \ diff --git a/cdist/conf/type/__mysql_database/explorer/state b/cdist/conf/type/__mysql_database/explorer/state new file mode 100755 index 00000000..79858695 --- /dev/null +++ b/cdist/conf/type/__mysql_database/explorer/state @@ -0,0 +1,33 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +if [ -f "$__object/parameter/name" ] +then + name="$( cat "$__object/parameter/name" )" +else + name="$__object_id" +fi + +if [ -n "$( mysql -B -N -e "show databases like '$name'" )" ] +then + echo 'present' +else + echo 'absent' +fi diff --git a/cdist/conf/type/__mysql_database/gencode-remote b/cdist/conf/type/__mysql_database/gencode-remote index 23e51b05..1bdb2b11 100755 --- a/cdist/conf/type/__mysql_database/gencode-remote +++ b/cdist/conf/type/__mysql_database/gencode-remote @@ -1,6 +1,6 @@ #!/bin/sh -e # -# 2012 Benedikt Koeppel (code@benediktkoeppel.ch) +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) # # This file is part of cdist. # @@ -17,38 +17,30 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# if --database was specified -if [ -f "$__object/parameter/name" ]; then - database="$(cat "$__object/parameter/name")" -else # otherwise use the object id as database name - database="$__object_id" +state_is="$( cat "$__object/explorer/state" )" + +state_should="$( cat "$__object/parameter/state" )" + +if [ "$state_is" = "$state_should" ] +then + exit 0 fi -cat <<-EOFF -mysql -u root <<-EOF - CREATE DATABASE IF NOT EXISTS $database -EOF -EOFF - -# if --user was specified -if [ -f "$__object/parameter/user" ]; then - user="$(cat "$__object/parameter/user")" - - # if --password was specified - if [ -f "$__object/parameter/password" ]; then - password="$(cat "$__object/parameter/password")" - cat <<-EOFF - mysql -u root <<-EOF - GRANT ALL PRIVILEGES ON $database.* to '$user'@'localhost' IDENTIFIED BY '$password'; -EOF -EOFF - else - cat <<-EOFF - mysql -u root <<-EOF - GRANT ALL PRIVILEGES ON $database.* to '$user'@'localhost'; -EOF -EOFF - fi +if [ -f "$__object/parameter/name" ] +then + name="$( cat "$__object/parameter/name" )" +else + name="$__object_id" fi + +case "$state_should" in + present) + echo "mysql -e 'create database \`$name\`'" + echo "create database $name" >> "$__messages_out" + ;; + absent) + echo "mysql -e 'drop database \`$name\`'" + echo "drop database $name" >> "$__messages_out" + ;; +esac diff --git a/cdist/conf/type/__mysql_database/man.rst b/cdist/conf/type/__mysql_database/man.rst index 1e245a08..b3b56b5f 100644 --- a/cdist/conf/type/__mysql_database/man.rst +++ b/cdist/conf/type/__mysql_database/man.rst @@ -8,24 +8,24 @@ cdist-type__mysql_database - Manage a MySQL database DESCRIPTION ----------- -This cdist type allows you to install a MySQL database. +Create MySQL database and optionally user with all privileges. -REQUIRED PARAMETERS -------------------- -None. OPTIONAL PARAMETERS ------------------- name - The name of the database to install - defaults to the object id + Name of database. Defaults to object id. user - A user that should have access to the database + Create user and give all privileges to database. password - The password for the user who manages the database + Password for user. + +state + Defaults to present. + If absent and user is also set, both will be removed (with privileges). EXAMPLES @@ -33,17 +33,23 @@ EXAMPLES .. code-block:: sh - __mysql_database "cdist" --name "cdist" --user "myuser" --password "mypwd" + # just create database + __mysql_database foo + + # create database with respective user with all privileges to database + __mysql_database bar \ + --user name \ + --password secret AUTHORS ------- -Benedikt Koeppel +Ander Punnar COPYING ------- -Copyright \(C) 2012 Benedikt Koeppel. You can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. +Copyright \(C) 2020 Ander Punnar. You can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) any +later version. diff --git a/cdist/conf/type/__mysql_database/manifest b/cdist/conf/type/__mysql_database/manifest new file mode 100755 index 00000000..a3c9ed5d --- /dev/null +++ b/cdist/conf/type/__mysql_database/manifest @@ -0,0 +1,52 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +if [ -f "$__object/parameter/user" ] +then + user="$( cat "$__object/parameter/user" )" +fi + +if [ -f "$__object/parameter/password" ] +then + password="$( cat "$__object/parameter/password" )" +fi + +if [ -n "$user" ] && [ -n "$password" ] +then + if [ -f "$__object/parameter/name" ] + then + database="$( cat "$__object/parameter/name" )" + else + database="$__object_id" + fi + + state_should="$( cat "$__object/parameter/state" )" + + __mysql_user "$user" \ + --password "$password" \ + --state "$state_should" + + # removing user should remove all user's privileges + require="__mysql_user/$user" \ + __mysql_privileges "$database/$user" \ + --database "$database" \ + --user "$user" \ + --state "$state_should" +fi diff --git a/cdist/conf/type/__mysql_database/parameter/default/state b/cdist/conf/type/__mysql_database/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__mysql_database/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__mysql_database/parameter/optional b/cdist/conf/type/__mysql_database/parameter/optional index 756afee7..6c0b1e85 100644 --- a/cdist/conf/type/__mysql_database/parameter/optional +++ b/cdist/conf/type/__mysql_database/parameter/optional @@ -1,3 +1,4 @@ name user password +state diff --git a/cdist/conf/type/__mysql_privileges/explorer/state b/cdist/conf/type/__mysql_privileges/explorer/state new file mode 100755 index 00000000..4f13a70c --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/explorer/state @@ -0,0 +1,40 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +privileges="$( cat "$__object/parameter/privileges" )" + +database="$( cat "$__object/parameter/database" )" + +table="$( cat "$__object/parameter/table" )" + +user="$( cat "$__object/parameter/user" )" + +host="$( cat "$__object/parameter/host" )" + +check_privileges="$( + mysql -B -N -e "show grants for '$user'@'$host'" \ + | grep -Ei "^grant $privileges on .$database.\..?$table.? to " || true )" + +if [ -n "$check_privileges" ] +then + echo 'present' +else + echo 'absent' +fi diff --git a/cdist/conf/type/__mysql_privileges/gencode-remote b/cdist/conf/type/__mysql_privileges/gencode-remote new file mode 100755 index 00000000..0656699f --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/gencode-remote @@ -0,0 +1,55 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +state_is="$( cat "$__object/explorer/state" )" + +state_should="$( cat "$__object/parameter/state" )" + +if [ "$state_is" = "$state_should" ] +then + exit 0 +fi + +privileges="$( cat "$__object/parameter/privileges" )" + +database="$( cat "$__object/parameter/database" )" + +table="$( cat "$__object/parameter/table" )" + +user="$( cat "$__object/parameter/user" )" + +host="$( cat "$__object/parameter/host" )" + +if [ "$table" != '*' ] +then + # shellcheck disable=SC2016 + table="$( printf '`%s`' "$table" )" +fi + +case "$state_should" in + present) + echo "mysql -e 'grant $privileges on \`$database\`.$table to \`$user\`@\`$host\`'" + echo "grant $privileges on $database.$table to $user@$host" >> "$__messages_out" + ;; + absent) + echo "mysql -e 'revoke $privileges on \`$database\`.$table from \`$user\`@\`$host\`'" + echo "revoke $privileges on $database.$table from $user@$host" >> "$__messages_out" + ;; +esac diff --git a/cdist/conf/type/__mysql_privileges/man.rst b/cdist/conf/type/__mysql_privileges/man.rst new file mode 100644 index 00000000..b72c9eba --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/man.rst @@ -0,0 +1,57 @@ +cdist-type__mysql_privileges(7) +=============================== + +NAME +---- +cdist-type__mysql_privileges - Manage MySQL privileges + + +DESCRIPTION +----------- + +Grant and revoke privileges of MySQL user. + + +REQUIRED PARAMETERS +------------------- +database + Name of database. + +user + Name of user. + + +OPTIONAL PARAMETERS +------------------- +privileges + Defaults to "all". + +table + Defaults to "*". + +host + Defaults to localhost. + +state + "present" grants and "absent" revokes. Defaults to present. + + +EXAMPLES +-------- + +.. code-block:: sh + + __mysql_privileges user-to-db --database db --user user + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2020 Ander Punnar. You can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) any +later version. diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/host b/cdist/conf/type/__mysql_privileges/parameter/default/host new file mode 100644 index 00000000..2fbb50c4 --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/default/host @@ -0,0 +1 @@ +localhost diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/privileges b/cdist/conf/type/__mysql_privileges/parameter/default/privileges new file mode 100644 index 00000000..5472efad --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/default/privileges @@ -0,0 +1 @@ +all privileges diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/state b/cdist/conf/type/__mysql_privileges/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__mysql_privileges/parameter/default/table b/cdist/conf/type/__mysql_privileges/parameter/default/table new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/default/table @@ -0,0 +1 @@ +* diff --git a/cdist/conf/type/__mysql_privileges/parameter/optional b/cdist/conf/type/__mysql_privileges/parameter/optional new file mode 100644 index 00000000..d4ed5bc5 --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/optional @@ -0,0 +1,4 @@ +privileges +table +host +state diff --git a/cdist/conf/type/__mysql_privileges/parameter/required b/cdist/conf/type/__mysql_privileges/parameter/required new file mode 100644 index 00000000..152b4a1e --- /dev/null +++ b/cdist/conf/type/__mysql_privileges/parameter/required @@ -0,0 +1,2 @@ +database +user diff --git a/cdist/conf/type/__mysql_user/explorer/state b/cdist/conf/type/__mysql_user/explorer/state new file mode 100755 index 00000000..6817ee9d --- /dev/null +++ b/cdist/conf/type/__mysql_user/explorer/state @@ -0,0 +1,54 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +if [ -f "$__object/parameter/name" ] +then + name="$( cat "$__object/parameter/name" )" +else + name="$__object_id" +fi + +if [ -f "$__object/parameter/password" ] +then + password="$( cat "$__object/parameter/password" )" +else + password='' +fi + +host="$( cat "$__object/parameter/host" )" + +check_user="$( mysql -B -N -e "select user from mysql.user where user = '$name' and host = '$host'" )" + +if [ -n "$check_user" ] +then + if [ -n "$password" ] + then + check_password="$( mysql -B -N -e "select user from mysql.user where user = '$name' and host = '$host' and password = password( '$password' )" )" + fi + + if [ -n "$password" ] && [ -z "$check_password" ] + then + echo 'change-password' + else + echo 'present' + fi +else + echo 'absent' +fi diff --git a/cdist/conf/type/__mysql_user/gencode-remote b/cdist/conf/type/__mysql_user/gencode-remote new file mode 100755 index 00000000..5f13bc87 --- /dev/null +++ b/cdist/conf/type/__mysql_user/gencode-remote @@ -0,0 +1,68 @@ +#!/bin/sh -e +# +# 2020 Ander Punnar (ander-at-kvlt-dot-ee) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +state_is="$( cat "$__object/explorer/state" )" + +state_should="$( cat "$__object/parameter/state" )" + +if [ "$state_is" = "$state_should" ] +then + exit 0 +fi + +if [ -f "$__object/parameter/name" ] +then + name="$( cat "$__object/parameter/name" )" +else + name="$__object_id" +fi + +host="$( cat "$__object/parameter/host" )" + +if [ -f "$__object/parameter/password" ] +then + password="$( cat "$__object/parameter/password" )" +else + if [ "$state_should" = 'present' ] + then + echo '--password needed' >&2 + exit 1 + else + password='' + fi +fi + +if [ "$state_is" = 'absent' ] && [ "$state_should" = 'present' ] +then + echo "mysql -e 'create user \`$name\`@\`$host\` identified by \"$password\"'" + echo "create user $name@$host" >> "$__messages_out" + +elif [ "$state_is" != 'absent' ] && [ "$state_should" = 'absent' ] +then + echo "mysql -e 'drop user \`$name\`@\`$host\`'" + echo "drop user $name@$host" >> "$__messages_out" + +elif [ "$state_is" = 'change-password' ] +then + # this only works with MySQL 5.7.6 and later or MariaDB 10.1.20 and later + echo "mysql -e 'alter user \`$name\`@\`$host\` identified by \"$password\"'" + echo "mysql -e 'flush privileges'" + echo "change password $name@$host" >> "$__messages_out" +fi diff --git a/cdist/conf/type/__mysql_user/man.rst b/cdist/conf/type/__mysql_user/man.rst new file mode 100644 index 00000000..c2b222d5 --- /dev/null +++ b/cdist/conf/type/__mysql_user/man.rst @@ -0,0 +1,48 @@ +cdist-type__mysql_user(7) +========================= + +NAME +---- +cdist-type__mysql_user - Manage a MySQL user + + +DESCRIPTION +----------- + +Create MySQL user or change password for the user. + + +OPTIONAL PARAMETERS +------------------- +name + Name of user. Defaults to object id. + +host + Host of user. Defaults to localhost. + +password + Password of user. + +state + Defaults to present. + + +EXAMPLES +-------- + +.. code-block:: sh + + __mysql_user user --password secret + + +AUTHORS +------- +Ander Punnar + + +COPYING +------- +Copyright \(C) 2020 Ander Punnar. You can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) any +later version. diff --git a/cdist/conf/type/__mysql_user/parameter/default/host b/cdist/conf/type/__mysql_user/parameter/default/host new file mode 100644 index 00000000..2fbb50c4 --- /dev/null +++ b/cdist/conf/type/__mysql_user/parameter/default/host @@ -0,0 +1 @@ +localhost diff --git a/cdist/conf/type/__mysql_user/parameter/default/state b/cdist/conf/type/__mysql_user/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__mysql_user/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__mysql_user/parameter/optional b/cdist/conf/type/__mysql_user/parameter/optional new file mode 100644 index 00000000..a286266c --- /dev/null +++ b/cdist/conf/type/__mysql_user/parameter/optional @@ -0,0 +1,4 @@ +name +host +password +state diff --git a/cdist/conf/type/__openldap_server/gencode-remote b/cdist/conf/type/__openldap_server/gencode-remote new file mode 100644 index 00000000..b1e98f8c --- /dev/null +++ b/cdist/conf/type/__openldap_server/gencode-remote @@ -0,0 +1,44 @@ +#!/bin/sh + +manager_dn=$(cat "${__object}/parameter/manager-dn") +manager_password=$(cat "${__object}/parameter/manager-password") +description=$(cat "${__object}/parameter/description") +suffix=$(cat "${__object}/parameter/suffix") +suffix_dc=$(printf "%s" "${suffix}" | awk -F',' '{print $1}' | awk -F'=' '{print $2}') + +SLAPD_IPC=$(tr '\n' ' ' < "${__object}/parameter/slapd-url" | awk '{ print $1}') + +cat <&1 > /dev/null; then + # Already exists, use ldapmodify + ldapmodify -xZ -D "${manager_dn}" -w "${manager_password}" -H '${SLAPD_IPC}' < +Evilham + + +COPYING +------- +Copyright \(C) 2020 ungleich glarus ag. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__openldap_server/manifest b/cdist/conf/type/__openldap_server/manifest new file mode 100644 index 00000000..2aeece26 --- /dev/null +++ b/cdist/conf/type/__openldap_server/manifest @@ -0,0 +1,297 @@ +#!/bin/sh + +name="${__target_host}" +manager_dn=$(cat "${__object}/parameter/manager-dn") +manager_password_hash=$(cat "${__object}/parameter/manager-password-hash") +serverid=$(cat "${__object}/parameter/serverid") +suffix=$(cat "${__object}/parameter/suffix") +slapd_modules=$(cat "${__object}/parameter/module" 2>/dev/null || true) +schemas=$(cat "${__object}/parameter/schema") +slapd_urls=$(tr '\n' ' ' < "${__object}/parameter/slapd-url") +tls_cipher_suite=$(cat "${__object}/parameter/tls-cipher-suite" 2>/dev/null || true) +extra_config=$(cat "${__object}/parameter/extra-config" || true) + + +os="$(cat "${__global}/explorer/os")" + +# Setup OS-dependent vars +CONF_OWNER="root" +CONF_GROUP="root" +case "${os}" in + freebsd) + PKGS="openldap-server" + ETC="/usr/local/etc" + SLAPD_DIR="/usr/local/etc/openldap" + SLAPD_DATA_DIR="/var/db/openldap-data" + SLAPD_RUN_DIR="/var/run/openldap" + SLAPD_MODULE_PATH="/usr/local/libexec/openldap" + SLAPD_MODULE_TYPE="la" + if [ -z "${slapd_modules}" ]; then + # It looks like ppolicy and syncprov must be compiled + slapd_modules="back_mdb back_monitor" + fi + CONF_OWNER="ldap" + CONF_GROUP="ldap" + if [ -z "${tls_cipher_suite}" ]; then + # TODO: research default for FreeBSD. 'NORMAL' appears to not work + tls_cipher_suite="HIGH:MEDIUM:+SSLv2" + fi + ;; + debian|ubuntu|devuan) + PKGS="slapd ldap-utils" + ETC="/etc" + SLAPD_DIR="/etc/ldap" + SLAPD_DATA_DIR="/var/lib/ldap" + SLAPD_RUN_DIR="/var/run/slapd" + SLAPD_MODULE_PATH="/usr/lib/ldap" + SLAPD_MODULE_TYPE="la" + if [ -z "${slapd_modules}" ]; then + slapd_modules="back_mdb ppolicy syncprov back_monitor" + fi + CONF_OWNER="openldap" + CONF_GROUP="openldap" + if [ -z "${tls_cipher_suite}" ]; then + tls_cipher_suite="NORMAL" + fi + ;; + alpine) + PKGS="openldap openldap-clients" + ETC="/etc" + SLAPD_DIR="/etc/openldap" + SLAPD_DATA_DIR="/var/lib/openldap" + SLAPD_RUN_DIR="/var/run/openldap" + SLAPD_MODULE_PATH="/usr/lib/openldap" + SLAPD_MODULE_TYPE="so" + if [ -z "${slapd_modules}" ]; then + slapd_modules="back_mdb ppolicy syncprov back_monitor" + PKGS="$PKGS openldap-back-mdb openldap-back-monitor openldap-overlay-all" + fi + CONF_OWNER="ldap" + CONF_GROUP="$SLAPD_USER" + if [ -z "${tls_cipher_suite}" ]; then + tls_cipher_suite="DEFAULT" + fi + ;; + *) + echo "Don't know the openldap defaults for: $os" >&2 + exit 1 + ;; +esac + +PKG_MAIN=$(echo "${PKGS}" | awk '{print $1;}') + + +# Determine if __letsencrypt_cert is to be used and setup vars accordingly +if [ -f "${__object}/parameter/tls-cert" ]; then + tls_cert=$(cat "${__object}/parameter/tls-cert") + + if [ ! -f "${__object}/parameter/tls-privkey" ]; then + echo "When tls-cert is defined, tls-privkey is also required." >&2 + exit 1 + fi + tls_privkey=$(cat "${__object}/parameter/tls-privkey") + + if [ ! -f "${__object}/parameter/tls-ca" ]; then + echo "When tls-cert is defined, tls-ca is also required." >&2 + exit 1 + fi + tls_ca=$(cat "${__object}/parameter/tls-ca") + + _skip_letsencrypt_cert="YES" +else + if [ ! -f "${__object}/parameter/admin-email" ]; then + echo "When using __letsencrypt_cert, admin-email is also required." >&2 + exit 1 + fi + admin_email=$(cat "${__object}/parameter/admin-email") + + tls_cert="${SLAPD_DIR}/sasl2/cert.pem" + tls_privkey="${SLAPD_DIR}/sasl2/privkey.pem" + tls_ca="${SLAPD_DIR}/sasl2/chain.pem" +fi + +mkdir "${__object}/files" +ldapconf="${__object}/files/ldapconf" + +replication="" +if [ -f "${__object}/parameter/replicate" ]; then + replication=yes + + if [ ! -f "${__object}/parameter/syncrepl-searchbase" ]; then + echo "Requiring the searchbase for replication" >&2 + exit 1 + fi + syncrepl_searchbase=$(cat "${__object}/parameter/syncrepl-searchbase") + + if [ ! -f "${__object}/parameter/syncrepl-credentials" ]; then + echo "Requiring credentials for replication" >&2 + exit 1 + fi + + syncrepl_credentials=$(cat "${__object}/parameter/syncrepl-credentials") + + if [ ! -f "${__object}/parameter/syncrepl-host" ]; then + echo "Requiring host(s) for replication" >&2 + exit 1 + fi + syncrepl_hosts=$(cat "${__object}/parameter/syncrepl-host") + +fi + +# Install required packages +for pkg in ${PKGS}; do + __package "${pkg}" +done + + +require="__package/${PKG_MAIN}" __start_on_boot slapd + +# Setup -h flag for the listeners. See man slapd (-h flag). +case "${os}" in + freebsd) + require="__start_on_boot/slapd" __key_value \ + --file "/etc/rc.conf" \ + --key "slapd_flags" \ + --value "\"-h '${slapd_urls}'\"" \ + --delimiter "=" \ + --comment "# LDAP Listener URLs" \ + "${__target_host}__slapd_flags" + ;; + debian|ubuntu|devuan) + require="__package/${PKG_MAIN}" __line rm_slapd_conf \ + --file ${ETC}/default/slapd \ + --regex 'SLAPD_CONF=.*' \ + --state absent + + require="__package/${PKG_MAIN}" __line rm_slapd_services \ + --file ${ETC}/default/slapd \ + --regex 'SLAPD_SERVICES=.*' \ + --state absent + + require="__line/rm_slapd_conf" __line add_slapd_conf \ + --file ${ETC}/default/slapd \ + --line "SLAPD_CONF=${SLAPD_DIR}/slapd.conf" \ + --state present + + require="__line/rm_slapd_services" __line add_slapd_services \ + --file ${ETC}/default/slapd \ + --line "SLAPD_SERVICES=\"${slapd_urls}\"" \ + --state present + ;; + alpine) + require="__package/${PKG_MAIN}" __line add_slapd_services \ + --file ${ETC}/conf.d/slapd \ + --line "command_args=\"-h '${slapd_urls}'\"" \ + --state present + ;; + *) + # Nothing to do here, move on. + ;; +esac + + +if [ -z "${_skip_letsencrypt_cert}" ]; then + if [ -f "${__object}/parameter/staging" ]; then + staging="--staging" + else + staging="" + fi + + # shellcheck disable=SC2086 + __directory ${SLAPD_DIR}/sasl2 + require="__directory/${SLAPD_DIR}/sasl2" __letsencrypt_cert "${name}" \ + --admin-email "${admin_email}" \ + --renew-hook "cp ${ETC}/letsencrypt/live/${name}/*.pem ${SLAPD_DIR}/sasl2 && chown -R ${CONF_OWNER}:${CONF_GROUP} ${SLAPD_DIR}/sasl2 && service slapd restart" \ + --automatic-renewal "${staging}" +fi + +require="__package/${PKG_MAIN}" __directory ${SLAPD_DIR}/slapd.d --state absent + +if [ -z "${_skip_letsencrypt_cert}" ]; then + require="__package/${PKG_MAIN} __letsencrypt_cert/${name}" \ + __file "${SLAPD_DIR}/slapd.conf" --owner "${CONF_OWNER}" --group "${CONF_GROUP}" --mode 644 \ + --source "${ldapconf}" +else + require="__package/${PKG_MAIN}" \ + __file "${SLAPD_DIR}/slapd.conf" --owner "${CONF_OWNER}" --group "${CONF_GROUP}" --mode 644 \ + --source "${ldapconf}" +fi + +# Start slapd.conf +cat << EOF > "${ldapconf}" +pidfile ${SLAPD_RUN_DIR}/slapd.pid +argsfile ${SLAPD_RUN_DIR}/slapd.args + +TLSCipherSuite ${tls_cipher_suite} +TLSCertificateFile ${tls_cert} +TLSCertificateKeyFile ${tls_privkey} +TLSCACertificateFile ${tls_ca} + +disallow bind_anon +require bind +security tls=1 +EOF + +# Add specified schemas +for schema in ${schemas}; do + echo "include ${SLAPD_DIR}/schema/${schema}.schema" >> "${ldapconf}" +done + +# Add specified modules +echo "modulepath ${SLAPD_MODULE_PATH}" >> "${ldapconf}" +for module in ${slapd_modules}; do + echo "moduleload ${module}.${SLAPD_MODULE_TYPE}" >> "${ldapconf}" +done + +# Rest of the config +cat << EOF >> "${ldapconf}" +loglevel 1024 + +database mdb +maxsize 1073741824 + +suffix "${suffix}" +directory ${SLAPD_DATA_DIR} +rootdn "${manager_dn}" +rootpw "${manager_password_hash}" + +index objectClass eq,pres +index ou,cn,mail,surname,givenname eq,pres,sub +index uidNumber,gidNumber,loginShell eq,pres +index uid,memberUid eq,pres,sub +index nisMapName,nisMapEntry eq,pres,sub +index entryCSN,entryUUID eq + +${extra_config} + +serverid ${serverid} +EOF + +# Setup replication +if [ "${replication}" ]; then + rid=1; + for syncrepl in ${syncrepl_hosts}; do + cat <> "${ldapconf}" +syncrepl rid=${rid} + provider=ldap://${syncrepl} + bindmethod=simple + starttls=yes + binddn="${manager_dn}" + credentials=${syncrepl_credentials} + searchbase="${syncrepl_searchbase}" + type=refreshAndPersist + retry="5 + 5 +" + interval=00:00:00:05 +EOF + rid=$((rid + 1)) + done + cat <> "${ldapconf}" +mirrormode true +overlay syncprov +syncprov-checkpoint 100 5 +syncprov-sessionlog 100 + +database monitor +limits dn.exact="${manager_dn}" time=unlimited size=unlimited +EOF +fi diff --git a/cdist/conf/type/__openldap_server/parameter/boolean b/cdist/conf/type/__openldap_server/parameter/boolean new file mode 100644 index 00000000..45056fe9 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/boolean @@ -0,0 +1,2 @@ +staging +replicate diff --git a/cdist/conf/type/__openldap_server/parameter/default/description b/cdist/conf/type/__openldap_server/parameter/default/description new file mode 100644 index 00000000..6d8e37e1 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/default/description @@ -0,0 +1 @@ +Managed by cdist, do not edit manually. diff --git a/cdist/conf/type/__openldap_server/parameter/default/schema b/cdist/conf/type/__openldap_server/parameter/default/schema new file mode 100644 index 00000000..825bdb15 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/default/schema @@ -0,0 +1,12 @@ +corba +core +cosine +duaconf +dyngroup +inetorgperson +java +misc +nis +openldap +ppolicy +collective diff --git a/cdist/conf/type/__openldap_server/parameter/optional b/cdist/conf/type/__openldap_server/parameter/optional new file mode 100644 index 00000000..71c64659 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/optional @@ -0,0 +1,9 @@ +description +syncrepl-credentials +syncrepl-searchbase +admin-email +tls-cipher-suite +tls-cert +tls-privkey +tls-ca +extra-config diff --git a/cdist/conf/type/__openldap_server/parameter/optional_multiple b/cdist/conf/type/__openldap_server/parameter/optional_multiple new file mode 100644 index 00000000..52a83d5c --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/optional_multiple @@ -0,0 +1,3 @@ +syncrepl-host +module +schema diff --git a/cdist/conf/type/__openldap_server/parameter/required b/cdist/conf/type/__openldap_server/parameter/required new file mode 100644 index 00000000..ff58158d --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/required @@ -0,0 +1,5 @@ +manager-dn +manager-password +manager-password-hash +serverid +suffix diff --git a/cdist/conf/type/__openldap_server/parameter/required_multiple b/cdist/conf/type/__openldap_server/parameter/required_multiple new file mode 100644 index 00000000..848b8dc2 --- /dev/null +++ b/cdist/conf/type/__openldap_server/parameter/required_multiple @@ -0,0 +1 @@ +slapd-url \ No newline at end of file diff --git a/cdist/conf/type/__openldap_server/singleton b/cdist/conf/type/__openldap_server/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__package/explorer/pkgng_exists b/cdist/conf/type/__package/explorer/pkgng_exists index 355c5d65..6d69ba14 100755 --- a/cdist/conf/type/__package/explorer/pkgng_exists +++ b/cdist/conf/type/__package/explorer/pkgng_exists @@ -21,7 +21,7 @@ # Retrieve the status of a package - parsed dpkg output # -if [ "$($__explorer/os)" = "freebsd" ]; then +if [ "$("$__explorer/os")" = "freebsd" ]; then command -v pkg fi diff --git a/cdist/conf/type/__package/manifest b/cdist/conf/type/__package/manifest index fe7abedc..a453c32b 100755 --- a/cdist/conf/type/__package/manifest +++ b/cdist/conf/type/__package/manifest @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2011-2013 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # @@ -44,6 +45,7 @@ else suse) type="zypper" ;; openwrt) type="opkg" ;; openbsd) type="pkg_openbsd" ;; + alpine) type="apk" ;; *) echo "Don't know how to manage packages on: $os" >&2 exit 1 @@ -55,8 +57,8 @@ state="$(cat "$__object/parameter/state")" set -- "$@" "$__object_id" "--state" "$state" cd "$__object/parameter" -for property in $(ls .); do - if [ "$property" != "type" -a "$property" != "state" ]; then +for property in *; do + if [ "$property" != "type" ] && [ "$property" != "state" ]; then set -- "$@" "--$property" "$(cat "$property")" fi done diff --git a/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_version b/cdist/conf/type/__package_apk/explorer/state similarity index 70% rename from cdist/conf/type/__package_pkg_openbsd/explorer/pkg_version rename to cdist/conf/type/__package_apk/explorer/state index 212f0d96..b477ca7c 100755 --- a/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_version +++ b/cdist/conf/type/__package_apk/explorer/state @@ -1,7 +1,6 @@ #!/bin/sh # -# 2011 Andi Brönnimann (andi-cdist at v-net.ch) -# Copyright 2017, Philippe Gregoire +# 2019 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # @@ -19,7 +18,7 @@ # along with cdist. If not, see . # # -# Retrieve the status of a package - parsed dpkg output +# Retrieve the status of a package - parsed apk output # if [ -f "$__object/parameter/name" ]; then @@ -28,5 +27,12 @@ else name="$__object_id" fi -#TODO: Is there a better way? -pkg_info | grep "^$name-[0-9]" | sed 's|.*\(-[0-9][0-9.]*\).*|\1|' | sed 's/-//' +# Remove the @.. repo tag for finding out whether it is installed +# f.i. pass@testing => pass +name="$(echo "$name" | sed 's/@.*//')" + +if [ "$(apk list -I "$name")" ]; then + echo present +else + echo absent +fi diff --git a/cdist/conf/type/__package_apk/gencode-remote b/cdist/conf/type/__package_apk/gencode-remote new file mode 100755 index 00000000..79e3d2b6 --- /dev/null +++ b/cdist/conf/type/__package_apk/gencode-remote @@ -0,0 +1,49 @@ +#!/bin/sh -e +# +# 2019 Nico Schottelius (nico-cdist at schottelius.org) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Manage packages on Debian and co. +# + +if [ -f "$__object/parameter/name" ]; then + name="$(cat "$__object/parameter/name")" +else + name="$__object_id" +fi + +state_should="$(cat "$__object/parameter/state")" +state_is="$(cat "$__object/explorer/state")" + +# Nothing to be done +[ "$state_is" = "$state_should" ] && exit 0 + +case "$state_should" in + present) + echo "apk add -q '$name'" + echo "installed" >> "$__messages_out" + ;; + absent) + echo "apk del -q '$name'" + echo "removed" >> "$__messages_out" + ;; + *) + echo "Unknown state: $state_should" >&2 + exit 1 + ;; +esac diff --git a/cdist/conf/type/__package_apk/man.rst b/cdist/conf/type/__package_apk/man.rst new file mode 100644 index 00000000..bc2408b4 --- /dev/null +++ b/cdist/conf/type/__package_apk/man.rst @@ -0,0 +1,55 @@ +cdist-type__package_akp(7) +========================== + +NAME +---- +cdist-type__package_akp - Manage packages with akp + + +DESCRIPTION +----------- +apk is usually used on Alpine to manage packages. + + +REQUIRED PARAMETERS +------------------- +None + + +OPTIONAL PARAMETERS +------------------- +name + If supplied, use the name and not the object id as the package name. + +state + Either "present" or "absent", defaults to "present" + + +EXAMPLES +-------- + +.. code-block:: sh + + # Ensure zsh in installed + __package_apk zsh --state present + + # Remove package + __package_apk apache2 --state absent + + +SEE ALSO +-------- +:strong:`cdist-type__package`\ (7) + + +AUTHORS +------- +Nico Schottelius + + +COPYING +------- +Copyright \(C) 2019 Nico Schottelius. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__package_apk/nonparallel b/cdist/conf/type/__package_apk/nonparallel new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__package_apk/parameter/default/state b/cdist/conf/type/__package_apk/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__package_apk/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__package_apk/parameter/optional b/cdist/conf/type/__package_apk/parameter/optional new file mode 100644 index 00000000..1b423dc4 --- /dev/null +++ b/cdist/conf/type/__package_apk/parameter/optional @@ -0,0 +1,2 @@ +name +state diff --git a/cdist/conf/type/__package_apt/explorer/state b/cdist/conf/type/__package_apt/explorer/state index 658429ac..7ccd6fce 100755 --- a/cdist/conf/type/__package_apt/explorer/state +++ b/cdist/conf/type/__package_apt/explorer/state @@ -30,7 +30,7 @@ fi # Except dpkg failing, if package is not known / installed packages="$(apt-cache showpkg "$name" | sed -e "1,/Reverse Provides:/d" | cut -d ' ' -f 1) $name" for p in $packages; do - if [ -n "$(dpkg -s "$p" 2>/dev/null | grep "^Status: install ok installed$")" ]; then + if dpkg -s "$p" 2>/dev/null | grep --quiet "^Status: install ok installed$" ; then version=$(dpkg -s "$p" 2>/dev/null | grep "^Version:" | cut -d ' ' -f 2) echo "present $p $version" exit 0 diff --git a/cdist/conf/type/__package_apt/gencode-remote b/cdist/conf/type/__package_apt/gencode-remote index d9cc52b7..fbfca330 100755 --- a/cdist/conf/type/__package_apt/gencode-remote +++ b/cdist/conf/type/__package_apt/gencode-remote @@ -42,6 +42,13 @@ else target_release="" fi +if [ -f "$__object/parameter/install-recommends" ]; then + # required if __apt_norecommends is used + recommendsparam="-o APT::Install-Recommends=1" +else + recommendsparam="-o APT::Install-Recommends=0" +fi + if [ -f "$__object/parameter/purge-if-absent" ]; then purgeparam="--purge" else @@ -62,25 +69,35 @@ case "$state_is" in ;; esac -# Hint if we need to avoid questions at some point: -# DEBIAN_PRIORITY=critical can reduce the number of questions -aptget="DEBIAN_FRONTEND=noninteractive apt-get --quiet --yes --no-install-recommends -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\"" - if [ "$state_is" = "$state_should" ]; then if [ -z "$version" ] || [ "$version" = "$version_is" ]; then exit 0; fi fi +# Hint if we need to avoid questions at some point: +# DEBIAN_PRIORITY=critical can reduce the number of questions +aptget="DEBIAN_FRONTEND=noninteractive apt-get --quiet --yes -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\"" + case "$state_should" in present) + # following is bit ugly, but important hack. + # due to how cdist config run works, there isn't + # currently better way to do it :( + cat << EOF +if [ ! -f /var/cache/apt/pkgcache.bin ] || [ "\$( stat --format %Y /var/cache/apt/pkgcache.bin )" -lt "\$( date +%s -d '-1 day' )" ] +then echo apt-get update > /dev/null 2>&1 || true +fi +EOF if [ -n "$version" ]; then name="${name}=${version}" fi - echo $aptget install $target_release \"$name\" + echo "$aptget $recommendsparam install $target_release '$name'" + echo "installed" >> "$__messages_out" ;; absent) - echo $aptget remove $purgeparam \"$name\" + echo "$aptget remove $purgeparam '$name'" + echo "removed" >> "$__messages_out" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/cdist/conf/type/__package_apt/man.rst b/cdist/conf/type/__package_apt/man.rst index a3a70d91..4e6101a5 100644 --- a/cdist/conf/type/__package_apt/man.rst +++ b/cdist/conf/type/__package_apt/man.rst @@ -9,7 +9,12 @@ cdist-type__package_apt - Manage packages with apt-get DESCRIPTION ----------- apt-get is usually used on Debian and variants (like Ubuntu) to -manage packages. +manage packages. The package will be installed without recommended +or suggested packages. If such packages are required, install them +separatly or use the parameter ``--install-recommends``. + +This type will also update package index, if it is older +than one day, to avoid missing package error messages. REQUIRED PARAMETERS @@ -20,7 +25,7 @@ None OPTIONAL PARAMETERS ------------------- name - If supplied, use the name and not the object id as the package name. + If supplied, use the name and not the object id as the package name. state Either "present" or "absent", defaults to "present" @@ -36,6 +41,15 @@ version BOOLEAN PARAMETERS ------------------ +install-recommends + If the package will be installed, it also installs recommended packages + with it. It will not install recommended packages if the original package + is already installed. + + In most cases, it is recommended to install recommended packages separatly + to control which additional packages will be installed to avoid useless + installed packages. + purge-if-absent If this parameter is given when state is `absent`, the package is purged from the system (using `--purge`). diff --git a/cdist/conf/type/__package_apt/parameter/boolean b/cdist/conf/type/__package_apt/parameter/boolean index f9a0f6b0..a2e433f3 100644 --- a/cdist/conf/type/__package_apt/parameter/boolean +++ b/cdist/conf/type/__package_apt/parameter/boolean @@ -1 +1,2 @@ +install-recommends purge-if-absent diff --git a/cdist/conf/type/__package_emerge/explorer/pkg_version b/cdist/conf/type/__package_emerge/explorer/pkg_version index 7053eaff..d02b9d6b 100644 --- a/cdist/conf/type/__package_emerge/explorer/pkg_version +++ b/cdist/conf/type/__package_emerge/explorer/pkg_version @@ -32,4 +32,5 @@ else name="$__object_id" fi +# shellcheck disable=SC2016 equery -q l -F '$cp $fullversion' "$name" || true diff --git a/cdist/conf/type/__package_emerge/gencode-remote b/cdist/conf/type/__package_emerge/gencode-remote index 6abe2d61..e1b85ebb 100755 --- a/cdist/conf/type/__package_emerge/gencode-remote +++ b/cdist/conf/type/__package_emerge/gencode-remote @@ -38,13 +38,13 @@ fi pkg_version="$(cat "$__object/explorer/pkg_version")" if [ -z "$pkg_version" ]; then state_is="absent" -elif [ -z "$version" -a $(echo "$pkg_version" | wc -l) -gt 1 ]; then - echo "Package name is not unique! The following packages are installed:" - echo "$pkg_version" +elif [ -z "$version" ] && [ "$(echo "$pkg_version" | wc -l)" -gt 1 ]; then + echo "Package name is not unique! The following packages are installed:" >&2 + echo "$pkg_version" >&2 exit 1 -elif [ -n "$version" -a $(echo "$pkg_version" | cut -d " " -f 1 | sort | uniq | wc -l) -gt 1 ]; then - echo "Package name is not unique! The following packages are installed:" - echo "$pkg_version" +elif [ -n "$version" ] && [ "$(echo "$pkg_version" | cut -d " " -f 1 | sort | uniq | wc -l)" -gt 1 ]; then + echo "Package name is not unique! The following packages are installed:" >&2 + echo "$pkg_version" >&2 exit 1 else state_is="present" @@ -57,16 +57,18 @@ fi # Exit if nothing is needed to be done -[ "$state_is" = "$state_should" ] && ( [ -z "$version" ] || [ "$installed_version" = "$version" ] ) && exit 0 -[ "$state_should" = "absent" ] && [ ! -z "$version" ] && [ "$installed_version" != "$version" ] && exit 0 +[ "$state_is" = "$state_should" ] && { [ -z "$version" ] || [ "$installed_version" = "$version" ]; } && exit 0 +[ "$state_should" = "absent" ] && [ -n "$version" ] && [ "$installed_version" != "$version" ] && exit 0 case "$state_should" in present) - echo "emerge \"$name\" &>/dev/null || exit 1" + echo "emerge '$name' &>/dev/null || exit 1" + echo "installed" >> "$__messages_out" ;; absent) - echo "emerge -C \"$name\" &>/dev/null || exit 1" + echo "emerge -C '$name' &>/dev/null || exit 1" + echo "removed" >> "$__messages_out" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/cdist/conf/type/__package_emerge_dependencies/gencode-remote b/cdist/conf/type/__package_emerge_dependencies/gencode-remote index face898a..f3e6f76e 100755 --- a/cdist/conf/type/__package_emerge_dependencies/gencode-remote +++ b/cdist/conf/type/__package_emerge_dependencies/gencode-remote @@ -6,10 +6,11 @@ flaggie_installed="$(cat "$__object/explorer/flaggie_installed")" if [ "${gentoolkit_installed}" != "true" ]; then # emerge app-portage/gentoolkit echo "emerge app-portage/gentoolkit &> /dev/null || exit 1" + echo "installed app-portage/gentoolkit" >> "$__messages_out" fi if [ "${flaggie_installed}" != "true" ]; then # emerge app-portage/flaggie echo "emerge app-portage/flaggie &> /dev/null || exit 1" + echo "installed app-portage/flaggie" >> "$__messages_out" fi - diff --git a/cdist/conf/type/__package_luarocks/explorer/pkg_status b/cdist/conf/type/__package_luarocks/explorer/pkg_status index 3eb73298..e83e8ce6 100755 --- a/cdist/conf/type/__package_luarocks/explorer/pkg_status +++ b/cdist/conf/type/__package_luarocks/explorer/pkg_status @@ -28,4 +28,4 @@ else fi # Accept luarocks failing if package is not known/installed -luarocks list "$name" | egrep -A1 "^$name$" || exit 0 +luarocks list "$name" | grep -E -A1 "^$name$" || exit 0 diff --git a/cdist/conf/type/__package_luarocks/gencode-remote b/cdist/conf/type/__package_luarocks/gencode-remote index cae06b22..d83b3c3a 100755 --- a/cdist/conf/type/__package_luarocks/gencode-remote +++ b/cdist/conf/type/__package_luarocks/gencode-remote @@ -42,10 +42,12 @@ fi case "$state_should" in present) - echo luarocks install \"$name\" + echo "luarocks install '$name'" + echo "installed" >> "$__messages_out" ;; absent) - echo luarocks remove \"$name\" + echo "luarocks remove '$name'" + echo "removed" >> "$__messages_out" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/cdist/conf/type/__package_opkg/explorer/pkg_status b/cdist/conf/type/__package_opkg/explorer/pkg_status index 5da4f742..de7b896b 100755 --- a/cdist/conf/type/__package_opkg/explorer/pkg_status +++ b/cdist/conf/type/__package_opkg/explorer/pkg_status @@ -1,7 +1,8 @@ -#!/bin/sh +#!/bin/sh -e # # 2011 Nico Schottelius (nico-cdist at schottelius.org) # 2012 Giel van Schijndel (giel plus cdist at mortis dot eu) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,21 +20,78 @@ # along with cdist. If not, see . # # -# Retrieve the status of a package - parsed opkg output +# Retrieve the status of a package - parses opkg output # -if [ -f "$__object/parameter/name" ]; then - name="$(cat "$__object/parameter/name")" +readonly __type_path=${__object%%${__object_id}*} +test -d "${__type_path}" || { echo 'Cannot determine __type_path' >&2; exit 1; } +readonly LOCKFILE="${__type_path:?}/.cdist_opkg.lock" + +if command -v flock >/dev/null 2>&1 +then + # use flock (if available) on FD 9 + _lock() { + exec 9<>"${LOCKFILE:?}" + flock -x 9 + echo $$>&9 + } + _unlock() { + :>"${LOCKFILE:?}" + flock -u 9 + exec 9<&- + } else - name="$__object_id" + # fallback to mkdir if flock is missing + _lock() { + until mkdir "${LOCKFILE:?}.dir" 2>/dev/null + do + while test -d "${LOCKFILE}.dir" + do + # DEBUG: + # printf 'Locked by PID: %u\n' "$(cat "${LOCKFILE}.dir/pid")" + sleep 1 + done + done + echo $$ >"${LOCKFILE:?}.dir/pid" + } + _unlock() { + test -d "${LOCKFILE}.dir" || return 0 + if test -s "${LOCKFILE}.dir/pid" + then + test "$(cat "${LOCKFILE}.dir/pid")" = $$ || return 1 + rm "${LOCKFILE:?}.dir/pid" + fi + rmdir "${LOCKFILE:?}.dir" + } fi -# Except dpkg failing, if package is not known / installed -if opkg status "$name" 2>/dev/null | grep -q "^Status: install user installed$"; then - echo "present" - exit 0 -elif [ "$(opkg info "$name" 2> /dev/null | wc -l)" -eq 0 ]; then - echo "absent notpresent" - exit 0 + +if test -f "${__object}/parameter/name" +then + pkg_name=$(cat "${__object}/parameter/name") +else + pkg_name=$__object_id +fi + + +# NOTE: We need to lock parallel execution of type explorers and code-remote +# because opkg will try to acquire the OPKG lock (usually /var/lock/opkg.lock) +# using lockf(2) for every operation. +# It will not wait for the lock but terminate with an error. +# This leads to incorrect 'absent notpresent' statuses when parallel execution +# is enabled. +trap _unlock EXIT +_lock + + +# Except opkg failing, if package is not known / installed +if opkg status "${pkg_name}" 2>/dev/null \ + | grep -q -e '^Status: [^ ][^ ]* [^ ][^ ]* installed$' +then + echo 'present' +elif opkg info "${pkg_name}" 2>/dev/null | grep -q . +then + echo 'absent notpresent' +else + echo 'absent' fi -echo "absent" diff --git a/cdist/conf/type/__package_opkg/gencode-remote b/cdist/conf/type/__package_opkg/gencode-remote index 09fe69a4..28caff71 100755 --- a/cdist/conf/type/__package_opkg/gencode-remote +++ b/cdist/conf/type/__package_opkg/gencode-remote @@ -2,6 +2,7 @@ # # 2011,2013 Nico Schottelius (nico-cdist at schottelius.org) # 2012 Giel van Schijndel (giel plus cdist at mortis dot eu) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,39 +20,50 @@ # along with cdist. If not, see . # # -# Manage packages on OpenWRT and co. +# Manage packages on OpenWrt, optware, and co. # -if [ -f "$__object/parameter/name" ]; then - name="$(cat "$__object/parameter/name")" +if test -f "${__object}/parameter/name" +then + name=$(cat "${__object}/parameter/name") else - name="$__object_id" + name=$__object_id fi -state_should="$(cat "$__object/parameter/state")" +state_should=$(cat "${__object}/parameter/state") +state_is=$(cat "${__object}/explorer/pkg_status") -state_is="$(cat "$__object/explorer/pkg_status")" -case "$state_is" in - absent*) - present="$(echo "$state_is" | cut -d ' ' -f 2)" - state_is="absent" - ;; +case $state_is +in + (absent*) + presence=$(echo "${state_is}" | cut -d ' ' -f 2) + state_is='absent' + ;; esac -[ "$state_is" = "$state_should" ] && exit 0 +if test "${state_is}" = "${state_should}" +then + exit 0 +fi -case "$state_should" in - present) - if [ "$present" = "notpresent" ]; then - echo opkg --verbosity=0 update - fi - echo opkg --verbosity=0 install \"$name\" - ;; - absent) - echo opkg --verbosity=0 remove \"$name\" - ;; - *) - echo "Unknown state: $state" >&2 - exit 1 - ;; + +case $state_should +in + (present) + if test "${presence}" = 'notpresent' + then + echo 'opkg --verbosity=0 update' + fi + + printf "opkg --verbosity=0 install '%s'\n" "${name}" + echo 'installed' >>"${__messages_out}" + ;; + (absent) + printf "opkg --verbosity=0 remove '%s'" "${name}" + echo 'removed' >>"${__messages_out}" + ;; + (*) + printf 'Unknown state: %s\n' "${state_should}" >&2 + exit 1 + ;; esac diff --git a/cdist/conf/type/__package_pacman/gencode-remote b/cdist/conf/type/__package_pacman/gencode-remote index 69a5d62a..2e076ec3 100755 --- a/cdist/conf/type/__package_pacman/gencode-remote +++ b/cdist/conf/type/__package_pacman/gencode-remote @@ -45,10 +45,12 @@ fi case "$state_should" in present) - echo pacman --needed --noconfirm --noprogressbar -S \"$name\" + echo "pacman --needed --noconfirm --noprogressbar -S '$name'" + echo "installed" >> "$__messages_out" ;; absent) - echo pacman --noconfirm --noprogressbar -R \"$name\" + echo "pacman --noconfirm --noprogressbar -R '$name'" + echo "removed" >> "$__messages_out" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/cdist/conf/type/__package_pip/explorer/pip b/cdist/conf/type/__package_pip/explorer/pip new file mode 100755 index 00000000..cf9fae89 --- /dev/null +++ b/cdist/conf/type/__package_pip/explorer/pip @@ -0,0 +1,10 @@ +#!/bin/sh -e + +for bin in pip3 pip +do + if check="$( command -v "$bin" )" + then + echo "$check" + break + fi +done diff --git a/cdist/conf/type/__package_pip/explorer/state b/cdist/conf/type/__package_pip/explorer/state index 5be07280..3cc98ab9 100644 --- a/cdist/conf/type/__package_pip/explorer/state +++ b/cdist/conf/type/__package_pip/explorer/state @@ -32,7 +32,7 @@ pipparam="$__object/parameter/pip" if [ -f "$pipparam" ]; then pip=$(cat "$pipparam") else - pip="pip" + pip="$( "$__type_explorer/pip" )" fi # If there is no pip, it may get created from somebody else. diff --git a/cdist/conf/type/__package_pip/gencode-remote b/cdist/conf/type/__package_pip/gencode-remote index 933406f2..a1375c2d 100755 --- a/cdist/conf/type/__package_pip/gencode-remote +++ b/cdist/conf/type/__package_pip/gencode-remote @@ -38,7 +38,12 @@ pipparam="$__object/parameter/pip" if [ -f "$pipparam" ]; then pip=$(cat "$pipparam") else - pip="pip" + pip="$( cat "$__object/explorer/pip" )" + if [ -z "$pip" ] + then + echo 'pip not found in path' >&2 + exit 1 + fi fi runasparam="$__object/parameter/runas" @@ -53,18 +58,20 @@ case "$state_should" in present) if [ "$runas" ] then - echo "su -c \"$pip install -q $name\" $runas" + echo "su -c '$pip install -q $name' $runas" else - echo $pip install -q "$name" + echo "$pip" install -q "$name" fi + echo "installed" >> "$__messages_out" ;; absent) if [ "$runas" ] then - echo "su -c \"$pip uninstall -q -y $name\" $runas" + echo "su -c '$pip uninstall -q -y $name' $runas" else - echo $pip uninstall -q -y "$name" + echo "$pip" uninstall -q -y "$name" fi + echo "removed" >> "$__messages_out" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/cdist/conf/type/__package_pkg_freebsd/explorer/pkg_version b/cdist/conf/type/__package_pkg_freebsd/explorer/pkg_version index 1335ba79..0a1ab75c 100755 --- a/cdist/conf/type/__package_pkg_freebsd/explorer/pkg_version +++ b/cdist/conf/type/__package_pkg_freebsd/explorer/pkg_version @@ -30,7 +30,7 @@ fi # Don't produce "no pkgs installed" output -- breaks things PKG_OUTPUT=$(pkg_info 2>&1) if [ ! "$PKG_OUTPUT" = "pkg_info: no packages installed" ]; then - echo -n "$(echo "$PKG_OUTPUT" \ + printf "%s" "$(echo "$PKG_OUTPUT" \ | awk '{print $1}' \ | sed 's/^\(.*\)-\([^-]*\)$/name:\1 ver:\2/g' \ | grep "name:$name ver:" \ diff --git a/cdist/conf/type/__package_pkg_freebsd/gencode-remote b/cdist/conf/type/__package_pkg_freebsd/gencode-remote index b51c3153..3f88f6bc 100755 --- a/cdist/conf/type/__package_pkg_freebsd/gencode-remote +++ b/cdist/conf/type/__package_pkg_freebsd/gencode-remote @@ -33,12 +33,13 @@ assert () # If condition false, lineno=$2 - if [ ! $1 ] + if [ ! "$1" ] then echo "Assertion failed: \"$1\"" + # shellcheck disable=SC2039 echo "File \"$0\", line $lineno, called by $(caller 0)" exit $E_ASSERT_FAILED - fi + fi } # Debug @@ -66,7 +67,7 @@ cmd="" # FIXME: This is ugly. execcmd(){ # Set the PACKAGESITE if we're ADDing a new package - if [ "$1" = "add" -a -n "$pkgsite" ]; then + if [ "$1" = "add" ] && [ -n "$pkgsite" ]; then # Use http.../All/ if we know the exact version we want, use .../Latest/ otherwise pkgsite="export PACKAGESITE=${pkgsite}" [ -n "$version" ] && pkgsite="${pkgsite}/All/" || pkgsite="${pkgsite}/Latest/" @@ -88,6 +89,7 @@ if [ -n "$curr_version" ]; then # PKG *is* installed cmd="${rm_cmd} ${name}-${curr_version}" fi execcmd "remove" "${cmd}" + echo "removed" >> "$__messages_out" exit 0 else # Should be installed if [ -n "$version" ]; then # Want a specific version @@ -95,11 +97,13 @@ if [ -n "$curr_version" ]; then # PKG *is* installed exit 0 else # Current version is wrong, fix #updatepkg "$name" "$version" + # shellcheck disable=SC2039 assert "! ${version} = ${curr_version}" $LINENO cmd="${rm_cmd} ${name}-${curr_version}" execcmd "remove" "${cmd}" cmd="${add_cmd} -r ${name}-${version}" execcmd "add" "${cmd}" + echo "installed" >> "$__messages_out" fi else # Don't care what version to use exit 0 @@ -118,6 +122,7 @@ else # PKG *isn't* installed cmd="${cmd}-${version}" fi execcmd "add" "${cmd}" + echo "installed" >> "$__messages_out" exit 0 fi fi diff --git a/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_state b/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_state new file mode 100755 index 00000000..9cd17787 --- /dev/null +++ b/cdist/conf/type/__package_pkg_openbsd/explorer/pkg_state @@ -0,0 +1,49 @@ +#!/bin/sh +# +# Copyright 2018, Takashi Yoshi +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Retrieve the status of a package - parsed pkg_info output +# + +if [ -f "${__object}/parameter/name" ] +then + pkgid="$(cat "${__object}/parameter/name")" +else + pkgid="${__object_id}" +fi + +if [ -f "${__object}/parameter/version" ] +then + pkgid="${pkgid}-$(cat "${__object}/parameter/version")" +fi + +if [ -f "${__object}/parameter/flavor" ] +then + # If a flavor but no version is given we need to add another -, + # otherwise pkg_info confuses the flavor with the version. + [ -f "${__object}/parameter/version" ] || pkgid="${pkgid}-" + + pkgid="${pkgid}-$(cat "${__object}/parameter/flavor")" +fi + + +pkg_info -q -I "inst:${pkgid}" >/dev/null 2>&1 \ + && echo 'present' || echo 'absent' + +exit 0 diff --git a/cdist/conf/type/__package_pkg_openbsd/gencode-remote b/cdist/conf/type/__package_pkg_openbsd/gencode-remote index 4a6763cd..5a21ce12 100755 --- a/cdist/conf/type/__package_pkg_openbsd/gencode-remote +++ b/cdist/conf/type/__package_pkg_openbsd/gencode-remote @@ -2,6 +2,7 @@ # # 2011 Andi Brönnimann (andi-cdist at v-net.ch) # 2012 Nico Schottelius (nico-cdist at schottelius.org) +# 2018 Takashi Yoshi # # This file is part of cdist. # @@ -22,105 +23,96 @@ # Manage packages with pkg on OpenBSD # -# Debug -# exec >&2 -# set -x +os_version=$(cat "${__global}/explorer/os_version") +machine=$(cat "${__global}/explorer/machine") -os_version="$(cat "$__global/explorer/os_version")" -machine="$(cat "$__global/explorer/machine")" - -if [ -f "$__object/parameter/version" ]; then - version="$(cat "$__object/parameter/version")" +if [ -f "${__object}/parameter/version" ]; then + version=$(cat "${__object}/parameter/version") fi -if [ -f "$__object/parameter/flavor" ]; then - flavor="$(cat "$__object/parameter/flavor")" +if [ -f "${__object}/parameter/flavor" ]; then + flavor=$(cat "${__object}/parameter/flavor") fi -# do not show progress bar -pkgopts="-x" +# Do not show progress bar +pkgopts='-x' -if [ -f "$__object/parameter/name" ]; then - name=$(cat "$__object/parameter/name") +name="${__object_id}" +if [ -f "${__object}/parameter/name" ]; then + name=$(cat "${__object}/parameter/name") +fi + +if [ -n "${version}" ] && [ -n "${flavor}" ]; then + pkgid="${name}-${version}-${flavor}" +elif [ -n "${version}" ]; then + pkgid="${name}-${version}" +elif [ -f "${__object}/parameter/flavor" ]; then + pkgid="${name}--${flavor}" else - name="$__object_id" + pkgid="${name}" fi -if [ -n "$version" -a -n "$flavor" ]; then - pkgid="$name-$version-$flavor" -elif [ -n "$version" ]; then - pkgid="$name-$version" -elif [ -n "$flavor" ]; then - pkgid="$name--$flavor" -elif [ -f "$__object/parameter/flavor" ]; then - pkgid="$name--" +state_should=$(cat "${__object}/parameter/state") + +if [ -f "${__object}/parameter/pkg_path" ]; then + pkg_path=$(cat "${__object}/parameter/pkg_path") else - pkgid="$name" + has_installurl=$(cat "${__object}/explorer/has_installurl") + if [ 'yes' != "${has_installurl}" ]; then + # There is no default PKG_PATH, try to provide one + pkg_path="ftp://ftp.openbsd.org/pub/OpenBSD/${os_version}/packages/${machine}/" + fi fi -state_should="$(cat "$__object/parameter/state")" +state_is=$(cat "${__object}/explorer/pkg_state") +[ "${state_is}" = "${state_should}" ] && exit 0 -pkg_version="$(cat "$__object/explorer/pkg_version")" +case "${state_should}" in + present) + if [ -n "${pkg_path}" ]; then + echo "export PKG_PATH='${pkg_path}'" + fi -if [ -f "$__object/parameter/pkg_path" ]; then - pkg_path="$(cat "$__object/parameter/pkg_path")" -else - has_installurl=$(cat "${__object}/explorer/has_installurl") - if [ Xyes != X"${has_installurl}" ]; then - # there is no default PKG_PATH, try to provide one - pkg_path="ftp://ftp.openbsd.org/pub/OpenBSD/$os_version/packages/$machine/" - fi -fi + # Use this because pkg_add doesn't properly handle errors + cat <&1 || true) -if [ "$pkg_version" ]; then - state_is="present" -else - state_is="absent" +if ! pkg_info -q -I 'inst:${pkgid}' | grep -q '^${name}-${version}.*${flavor}$' 2>/dev/null +then + # We didn't find the package in the list of 'installed packages', so it failed. + # This is necessary because pkg_add doesn't return properly + + if [ -z "\${status}" ]; then + status='Failed to add package, uncaught exception.' + fi + echo "Error: \${status}" >&2 + exit 1 fi +EOF + echo 'installed' >> "${__messages_out}" + ;; -[ "$state_is" = "$state_should" ] && exit 0 + absent) + # Use this because pkg_delete doesn't properly handle errors + cat <&1 || true) -case "$state_should" in - present) - # use this because pkg_add doesn't properly handle errors - cat << eof -if [ X != X"${pkg_path}" ]; then - PKG_PATH="${pkg_path}"; export PKG_PATH +if pkg_info -q -I 'inst:${pkgid}' | grep -q '^${name}-${version}.*${flavor}' 2>/dev/null +then + # We found the package in the list of 'installed packages'. + # This would indicate that pkg_delete failed, send the output of pkg_delete + + if [ -z "\${status}" ]; then + status='Failed to remove package, uncaught exception.' + fi + echo "Error: \${status}" >&2 + exit 1 fi -status=\$(pkg_add "$pkgopts" "$pkgid" 2>&1) -pkg_info | grep "^${name}.*${version}.*${flavor}" > /dev/null 2>&1 - -# We didn't find the package in the list of 'installed packages', so it failed -# This is necessary because pkg_add doesn't return properly -if [ \$? -ne 0 ]; then - if [ -z "\${status}" ]; then - status="Failed to add package, uncaught exception." - fi - echo "Error: \$status" - exit 1 -fi -eof - ;; - - absent) - # use this because pkg_add doesn't properly handle errors - cat << eof -status=\$(pkg_delete "$pkgopts" "$pkgid") -pkg_info | grep "^${name}.*${version}.*${flavor}" > /dev/null 2>&1 - -# We found the package in the list of 'installed packages' -# This would indicate that pkg_delete failed, send the output of pkg_delete -if [ \$? -eq 0 ]; then - if [ -z "\${status}" ]; then - status="Failed to remove package, uncaught exception." - fi - echo "Error: \$status" - exit 1 -fi -eof - ;; - *) - echo "Unknown state: $state_should" >&2 +EOF + echo 'removed' >> "${__messages_out}" + ;; + *) + echo "Unknown state: ${state_should}" >&2 exit 1 - ;; + ;; esac diff --git a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped new file mode 100755 index 00000000..429f15d3 --- /dev/null +++ b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped @@ -0,0 +1,4 @@ +#!/bin/sh -e +if pkg -N >/dev/null 2>&1; then + echo "YES" +fi diff --git a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version index 947857b9..1c6ba5e5 100755 --- a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version +++ b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version @@ -18,9 +18,14 @@ # along with cdist. If not, see . # # -# Retrieve the status of a package - parsed dpkg output +# Retrieve the status of a package - parsed pkgng output # +if ! pkg -N >/dev/null 2>&1; then + # Nothing to do if pkg is not bootstrapped + exit +fi + if [ -f "$__object/parameter/name" ]; then name="$(cat "$__object/parameter/name")" else @@ -29,7 +34,7 @@ fi # Don't produce "no pkgs installed" output -- breaks things PKG_OUTPUT=$(pkg info 2>&1) -echo -n "$(echo "$PKG_OUTPUT" \ +printf "%s" "$(echo "$PKG_OUTPUT" \ | awk '{print $1}' \ | sed 's/^\(.*\)-\([^-]*\)$/name:\1 ver:\2/g' \ | grep "name:$name ver:" \ diff --git a/cdist/conf/type/__package_pkgng_freebsd/gencode-remote b/cdist/conf/type/__package_pkgng_freebsd/gencode-remote index d21e9e2a..05ba4cb2 100755 --- a/cdist/conf/type/__package_pkgng_freebsd/gencode-remote +++ b/cdist/conf/type/__package_pkgng_freebsd/gencode-remote @@ -43,6 +43,7 @@ fi repo="$(cat "$__object/parameter/repo")" state="$(cat "$__object/parameter/state")" curr_version="$(cat "$__object/explorer/pkg_version")" +pkg_bootstrapped="$(cat "$__object/explorer/pkg_bootstrapped")" add_cmd="pkg install -y" rm_cmd="pkg delete -y" upg_cmd="pkg upgrade -y" @@ -52,17 +53,20 @@ cmd="" # Parms: $1 -- mode, "rm", "add", or "upg" # $2 -- the command to be echoed execcmd(){ - local _cmd="" + _cmd="" case "$1" in add) _cmd="${add_cmd} $2" + echo "installed" >> "$__messages_out" ;; rm) _cmd="${rm_cmd} $2" + echo "removed" >> "$__messages_out" ;; upg) _cmd="${upg_cmd} $2" + echo "installed" >> "$__messages_out" ;; *) printf "Error. Don't understand command: %s" "$1" >&2 @@ -70,6 +74,10 @@ execcmd(){ ;; esac + if [ -z "${pkg_bootstrapped}" ]; then + echo "ASSUME_ALWAYS_YES=yes pkg bootstrap >/dev/null 2>&1" + fi + echo "$_cmd >/dev/null 2>&1" # Silence the output of the command echo "status=\$?" echo "if [ \"\$status\" -ne \"0\" ]; then" @@ -95,7 +103,7 @@ if [ -n "$curr_version" ]; then # PKG *is* installed if [ "$upgrade" = "true" ]; then execcmd "upg" "${cmd}" else - printf "Version %s is already installed and pkg-ng can't upgrade directly to version %s.\nTo upgrade to the latest version, use the --upgrade flag.\n" "$curr_version" "$version" >&2 + printf 'Version %s is already installed and pkg-ng cannot upgrade directly to version %s.\nTo upgrade to the latest version, use the --upgrade flag.\n' "$curr_version" "$version" >&2 exit 1 fi # PKG is supposed to be installed to the latest version diff --git a/cdist/conf/type/__package_rubygem/gencode-remote b/cdist/conf/type/__package_rubygem/gencode-remote index 6d793ac0..abb40653 100755 --- a/cdist/conf/type/__package_rubygem/gencode-remote +++ b/cdist/conf/type/__package_rubygem/gencode-remote @@ -39,10 +39,12 @@ fi case "$state_should" in present) - echo gem install \"$name\" --no-ri --no-rdoc + echo "gem install '$name' --no-ri --no-rdoc" + echo "installed" >> "$__messages_out" ;; absent) - echo gem uninstall \"$name\" + echo "gem uninstall '$name'" + echo "removed" >> "$__messages_out" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/cdist/conf/type/__package_update_index/explorer/currage b/cdist/conf/type/__package_update_index/explorer/currage index cd042bd5..8eadaf53 100644 --- a/cdist/conf/type/__package_update_index/explorer/currage +++ b/cdist/conf/type/__package_update_index/explorer/currage @@ -17,18 +17,27 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . -os="$("$__explorer/os")" +type="$("$__type_explorer/type")" -case "$os" in - debian|ubuntu|devuan) +case "$type" in + apt) if [ -f "/var/cache/apt/pkgcache.bin" ]; then echo $(($(date +"%s")-$(stat --format '%Y' /var/cache/apt/pkgcache.bin))) else - echo 0 + echo -- -1 fi ;; - *) echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2 + pacman) + if [ -d "/var/lib/pacman/sync" ]; then + echo $(($(date +"%s")-$(stat --format '%Y' /var/lib/pacman/sync))) + else + echo -- -1 + fi + ;; + alpine) + echo -- -1 + ;; + *) echo "Your specified type ($type) is currently not supported." >&2 echo "Please contribute an implementation for it if you can." >&2 - exit 1 ;; esac diff --git a/cdist/conf/type/__pf_ruleset/explorer/cksum b/cdist/conf/type/__package_update_index/explorer/type old mode 100755 new mode 100644 similarity index 55% rename from cdist/conf/type/__pf_ruleset/explorer/cksum rename to cdist/conf/type/__package_update_index/explorer/type index f8679836..c98e1e67 --- a/cdist/conf/type/__pf_ruleset/explorer/cksum +++ b/cdist/conf/type/__package_update_index/explorer/type @@ -1,6 +1,6 @@ #!/bin/sh # -# 2012 Jake Guffey (jake.guffey at eprotex.com) +# 2018 Stu Zhao (z12y12l12 at gmail.com) # # This file is part of cdist. # @@ -16,26 +16,20 @@ # # You should have received a copy of the GNU General Public License # along with cdist. If not, see . -# -# -# Get the 256 bit SHA2 checksum of the pf ruleset on the target host. -# -# Debug -#exec >&2 -#set -x - -# Check /etc/rc.conf for pf's configuration file name. Default to /etc/pf.conf -# See if file exists and if so, get checksum - -RC="/etc/rc.conf" -TMP="$(grep '^pf_rules=' ${RC} | cut -d= -f2 | sed 's/"//g')" -PFCONF="${TMP:-"/etc/pf.conf"}" - -if [ -f "${PFCONF}" ]; then # The pf config file exists, find its cksum. - cksum -o 1 ${PFCONF} | cut -d= -f2 | awk '{print $1}' +if [ -f "$__object/parameter/type" ]; then + cat "$__object/parameter/type" +else + # By default determine package manager based on operating system + os="$("$__explorer/os")" + case "$os" in + amazon|scientific|centos|fedora|redhat) echo "yum" ;; + debian|ubuntu|devuan) echo "apt" ;; + archlinux) echo "pacman" ;; + alpine) echo "apk" ;; + *) + echo "Don't know how to manage packages on: $os" >&2 + exit 1 + ;; + esac fi - -# Debug -#set +x - diff --git a/cdist/conf/type/__package_update_index/gencode-remote b/cdist/conf/type/__package_update_index/gencode-remote index 37bfe7ab..803468b5 100755 --- a/cdist/conf/type/__package_update_index/gencode-remote +++ b/cdist/conf/type/__package_update_index/gencode-remote @@ -21,49 +21,39 @@ # Update the package index with the appropriate package manager # -type="$__object/parameter/type" +type=$(cat "$__object/explorer/type") +currage="$(cat "$__object/explorer/currage")" if [ -f "$__object/parameter/maxage" ]; then maxage="$(cat "$__object/parameter/maxage")" - currage="$(cat "$__object/explorer/currage")" fi -if [ -f "$type" ]; then - type="$(cat "$type")" -else - # By default determine package manager based on operating system - os="$(cat "$__global/explorer/os")" - case "$os" in - amazon|scientific|centos|fedora|redhat) type="yum" ;; - debian|ubuntu|devuan) type="apt" ;; - archlinux) type="pacman" ;; - *) - echo "Don't know how to manage packages on: $os" >&2 - exit 1 - ;; - esac +if [ -n "$maxage" ]; then + if [ "$type" != "apt" ] && [ "$type" != "pacman" ]; then + echo "ERROR: \"--maxage\" only supported for \"apt\" or \"pacman\" pkg-manager." >&2 + exit 1 + # do not exit if no value found (represented as -1) + elif [ "$currage" -ne -1 ] && [ "$currage" -lt "$maxage" ]; then + exit 0 # no need to update + fi fi -if [ -n "$maxage" ] && [ "$type" != "apt" ]; then - echo "ERROR: \"--maxage\" only supported for \"apt\" pkg-manager." >&2 - exit 1 -fi case "$type" in yum) ;; - apt) if [ -n "$maxage" ]; then - ## check if we need to update: - if [ $currage -ge $maxage ]; then - echo "apt-get --quiet update" - echo "apt-cache updated (age was: $currage)" >> "$__messages_out" - fi - else - echo "apt-get --quiet update" - echo "apt-cache updated (age was: $currage)" >> "$__messages_out" - fi - ;; - pacman) echo "pacman --noprogressbar --sync --refresh" ;; + apt) + echo "apt-get --quiet update" + echo "apt-cache updated (age was: $currage)" >> "$__messages_out" + ;; + pacman) + echo "pacman --noprogressbar --sync --refresh" + echo "pacman package database synced (age was: $currage)" >> "$__messages_out" + ;; + apk) + echo "apk update" + echo "apk package database updated." >>"$__messages_out" + ;; *) - echo "Don't know how to manage packages on: $os" >&2 + echo "Don't know how to manage packages for type: $type" >&2 exit 1 ;; esac diff --git a/cdist/conf/type/__package_update_index/man.rst b/cdist/conf/type/__package_update_index/man.rst index b63af654..3cd787b9 100644 --- a/cdist/conf/type/__package_update_index/man.rst +++ b/cdist/conf/type/__package_update_index/man.rst @@ -28,8 +28,8 @@ type * pacman for Arch Linux maxage - Available for package manager apt, max time in seconds since last update. - Repo update is skipped if maxage is not reached yet. + Available for package manager apt and pacman, max time in seconds since + last update. Repo update is skipped if maxage is not reached yet. MESSAGES -------- @@ -51,6 +51,7 @@ EXAMPLES # Only update every hour: __package_update_index --maxage 3600 --type apt + # same as above (on apt-type systems): __package_update_index --maxage 3600 @@ -58,6 +59,7 @@ AUTHORS ------- | Ricardo Catalinas Jiménez | Thomas Eckert +| Stu Zhao COPYING diff --git a/cdist/conf/type/__package_upgrade_all/gencode-remote b/cdist/conf/type/__package_upgrade_all/gencode-remote index bcad8a43..38aa001e 100755 --- a/cdist/conf/type/__package_upgrade_all/gencode-remote +++ b/cdist/conf/type/__package_upgrade_all/gencode-remote @@ -53,8 +53,8 @@ case "$type" in ;; apt) if [ -f "$apt_dist_upgrade" ] - then echo $aptget dist-upgrade - else echo $aptget upgrade + then echo "$aptget dist-upgrade" + else echo "$aptget upgrade" fi if [ -f "$apt_clean" ] diff --git a/cdist/conf/type/__package_yum/explorer/pkg_version b/cdist/conf/type/__package_yum/explorer/pkg_version index fb3b7753..b81b0fe9 100755 --- a/cdist/conf/type/__package_yum/explorer/pkg_version +++ b/cdist/conf/type/__package_yum/explorer/pkg_version @@ -27,4 +27,4 @@ else name="$__object_id" fi -rpm -q --whatprovides "$name" 2>/dev/null || true +rpm -q "$name" 2>/dev/null || rpm -q --whatprovides "$name" 2>/dev/null || true diff --git a/cdist/conf/type/__package_yum/gencode-remote b/cdist/conf/type/__package_yum/gencode-remote index e9b48ee8..b52953f6 100755 --- a/cdist/conf/type/__package_yum/gencode-remote +++ b/cdist/conf/type/__package_yum/gencode-remote @@ -43,10 +43,15 @@ else opts="--assumeyes --quiet" fi -not_installed="^no package provides" +not_provided="^no package provides" +not_installed='is not installed$' -if grep -q "$not_installed" "$__object/explorer/pkg_version"; then - state_is="absent" +if grep -q "$not_provided" "$__object/explorer/pkg_version"; then + if grep -q "$not_installed" "$__object/explorer/pkg_version"; then + state_is="absent" + else + state_is="present" + fi else state_is="present" fi @@ -55,10 +60,12 @@ fi case "$state_should" in present) - echo yum $opts install \"$install_name\" + echo "yum $opts install '$install_name'" + echo "installed" >> "$__messages_out" ;; absent) - echo yum $opts remove \"$name\" + echo "yum $opts remove '$name'" + echo "removed" >> "$__messages_out" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/cdist/conf/type/__package_zypper/gencode-remote b/cdist/conf/type/__package_zypper/gencode-remote index d9372b6d..e45dd9ff 100755 --- a/cdist/conf/type/__package_zypper/gencode-remote +++ b/cdist/conf/type/__package_zypper/gencode-remote @@ -61,15 +61,18 @@ case "$state_should" in present) if [ -z "$version_should" ]; then [ "$state_is" = "present" ] && exit 0 # if state is present, we dont need to do anything - echo zypper $globalopts install --type \"$ptype\" --auto-agree-with-licenses \"$name\" ">/dev/null" + echo "zypper $globalopts install --type '$ptype' --auto-agree-with-licenses '$name' >/dev/null" + echo "removed" >> "$__messages_out" else [ "$state_is" = "present" ] && [ "$version_should" = "$version_is" ] && exit 0 # if state is present and version is correct, we dont need to do anything - echo zypper $globalopts install --oldpackage --type \"$ptype\" --auto-agree-with-licenses \"$name\" = \"$version_should\" ">/dev/null" + echo "zypper $globalopts install --oldpackage --type '$ptype' --auto-agree-with-licenses '$name' = '$version_should' >/dev/null" + echo "installed" >> "$__messages_out" fi ;; absent) [ "$state_is" = "absent" ] && exit 0 # if state is absent, we dont need to do anything - echo zypper $globalopts remove --type \"$ptype\" \"$name\" ">/dev/null" + echo "zypper $globalopts remove --type '$ptype' '$name' >/dev/null" + echo "removed" >> "$__messages_out" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/cdist/conf/type/__pacman_conf/manifest b/cdist/conf/type/__pacman_conf/manifest index 1561d613..a43f18a1 100755 --- a/cdist/conf/type/__pacman_conf/manifest +++ b/cdist/conf/type/__pacman_conf/manifest @@ -59,13 +59,13 @@ if [ "${file}" ]; then if [ "${state}" = "present" ]; then - require="__file/${sec_path}/plain_file_${file}" __key_value ${file}_${key}\ - --file ${sec_path}/plain_file_${file} --key ${key} --value ${value} --delimiter ' = ' + require="__file/${sec_path}/plain_file_${file}" __key_value "${file}_${key}" \ + --file "${sec_path}/plain_file_${file}" --key "${key}" --value "${value}" --delimiter ' = ' exit 0 elif [ "${state}" = "absent" ]; then - require="__file/${sec_path}/plain_file_${file}" __key_value ${file}_${key}\ + require="__file/${sec_path}/plain_file_${file}" __key_value "${file}_${key}" \ --state absent exit 0 @@ -87,19 +87,19 @@ eof if [ "${MATCH}" -eq 1 ]; then if [ "${value}" = "on" ]; then - require="__file/${sec_path}/${section}" __line ${key}_${value}\ - --file ${sec_path}/${section} --line ${key} + require="__file/${sec_path}/${section}" __line "${key}_${value}" \ + --file "${sec_path}/${section}" --line "${key}" elif [ "${value}" = "off" ]; then - require="__file/${sec_path}/${section}" __line ${key}_${value}\ - --file ${sec_path}/${section} --line ${key} --state absent + require="__file/${sec_path}/${section}" __line "${key}_${value}" \ + --file "${sec_path}/${section}" --line "${key}" --state absent fi else contains_element "${key}" "${allowed_option_keys}" if [ "${MATCH}" -eq 1 ]; then - require="__file/${sec_path}/${section}" __key_value ${section}_${key}\ - --file ${sec_path}/${section} --key ${key} --value ${value} --delimiter ' = ' + require="__file/${sec_path}/${section}" __key_value "${section}_${key}" \ + --file "${sec_path}/${section}" --key "${key}" --value "${value}" --delimiter ' = ' else echo "Key: ${key} is not valid. Have a look at man pacman.conf" >&2 fi @@ -118,12 +118,12 @@ eof exit fi - require="__file/${sec_path}/repo_${section}" __key_value ${section}_${key}\ - --file ${sec_path}/repo_${section} --key ${key} --value ${value} --delimiter ' = ' + require="__file/${sec_path}/repo_${section}" __key_value "${section}_${key}" \ + --file "${sec_path}/repo_${section}" --key "${key}" --value "${value}" --delimiter ' = ' elif [ "${state}" = "absent" ]; then - require="__file/${sec_path}/repo_${section}" __key_value ${section}_${key}\ + require="__file/${sec_path}/repo_${section}" __key_value "${section}_${key}" \ --state absent else diff --git a/cdist/conf/type/__pacman_conf_integrate/manifest b/cdist/conf/type/__pacman_conf_integrate/manifest index b26bca50..0ce0bee5 100755 --- a/cdist/conf/type/__pacman_conf_integrate/manifest +++ b/cdist/conf/type/__pacman_conf_integrate/manifest @@ -18,16 +18,14 @@ # along with cdist. If not, see . # -state=$(cat $__object/parameter/state 2>/dev/null) - -path="/etc/" +state=$(cat "$__object/parameter/state" 2>/dev/null) if [ "${state}" = "present" ]; then __file /etc/pacman.conf\ - --owner root --group root --mode 644 --source $__type/files/pacman.conf.cdist + --owner root --group root --mode 644 --source "$__type/files/pacman.conf.cdist" __file /etc/pacman.d/options\ - --owner root --group root --mode 644 --source $__type/files/options + --owner root --group root --mode 644 --source "$__type/files/options" __file /etc/pacman.d/repo_empty_placeholder\ --owner root --group root --mode 644 @@ -38,10 +36,10 @@ if [ "${state}" = "present" ]; then elif [ "${state}" = "absent" ]; then __file /etc/pacman.conf\ - --owner root --group root --mode 644 --source $__type/files/pacman.conf.pacman + --owner root --group root --mode 644 --source "$__type/files/pacman.conf.pacman" __file /etc/pacman.d/mirrorlist\ - --owner root --group root --mode 644 --source $__type/files/mirrorlist + --owner root --group root --mode 644 --source "$__type/files/mirrorlist" __file /etc/pacman.d/options\ --state absent diff --git a/cdist/conf/type/__pf_apply/gencode-remote b/cdist/conf/type/__pf_apply/gencode-remote deleted file mode 100755 index c8f7a25a..00000000 --- a/cdist/conf/type/__pf_apply/gencode-remote +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh -e -# -# 2012 Jake Guffey (jake.guffey at eprotex.com) -# -# This file is part of cdist. -# -# cdist is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# cdist is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with cdist. If not, see . -# -# -# Apply pf(4) ruleset on *BSD -# - -# Debug -#exec >&2 -#set -x - -rcvar=$(cat "$__object/explorer/rcvar") - -cat <&2 - fi -fi -EOF - -# Debug -#set +x - diff --git a/cdist/conf/type/__pf_apply/man.rst b/cdist/conf/type/__pf_apply/man.rst deleted file mode 100644 index eee345e7..00000000 --- a/cdist/conf/type/__pf_apply/man.rst +++ /dev/null @@ -1,55 +0,0 @@ -cdist-type__pf_apply(7) -======================= - -NAME ----- -cdist-type__pf_apply - Apply pf(4) ruleset on \*BSD - - -DESCRIPTION ------------ -This type is used on \*BSD systems to manage the pf firewall's active ruleset. - - -REQUIRED PARAMETERS -------------------- -NONE - - -OPTIONAL PARAMETERS -------------------- -NONE - - -EXAMPLES --------- - -.. code-block:: sh - - # Modify the ruleset on $__target_host: - __pf_ruleset --state present --source /my/pf/ruleset.conf - require="__pf_ruleset" \ - __pf_apply - - # Remove the ruleset on $__target_host (implies disabling pf(4): - __pf_ruleset --state absent - require="__pf_ruleset" \ - __pf_apply - - -SEE ALSO --------- -:strong:`pf`\ (4), :strong:`cdist-type__pf_ruleset`\ (7) - - -AUTHORS -------- -Jake Guffey - - -COPYING -------- -Copyright \(C) 2012 Jake Guffey. You can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. diff --git a/cdist/conf/type/__pf_apply_anchor/gencode-remote b/cdist/conf/type/__pf_apply_anchor/gencode-remote new file mode 100755 index 00000000..36c26521 --- /dev/null +++ b/cdist/conf/type/__pf_apply_anchor/gencode-remote @@ -0,0 +1,33 @@ +#!/bin/sh -e +# +# 2016 Kamila Součková (coding at kamila.is) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Apply pf(4) ruleset on *BSD +# + +ANCHORS_DIR="/etc/pf.d" + +if [ -f "${__object}/parameter/anchor_name" ]; then + anchor_name="$(cat "${__object}/parameter/anchor_name")" +else + anchor_name="${__object_id}" +fi +anchor_file="${ANCHORS_DIR}/${anchor_name}" + +echo "pfctl -a \"${anchor_name}\" -f \"${anchor_file}\"" diff --git a/cdist/conf/type/__pf_apply_anchor/man.rst b/cdist/conf/type/__pf_apply_anchor/man.rst new file mode 100644 index 00000000..aef6cdf4 --- /dev/null +++ b/cdist/conf/type/__pf_apply_anchor/man.rst @@ -0,0 +1,62 @@ +cdist-type__pf_apply_anchor(7) +============================== + +NAME +---- +cdist-type__pf_apply_anchor - Apply a pf(4) anchor on $__target_host + + +DESCRIPTION +----------- +This type is used on \*BSD systems to manage anchors for the pf firewall. + +Notice this type does not take care of copying the ruleset, that must be +done by the user with, e.g. `__file`. + + +OPTIONAL PARAMETERS +------------------- +anchor_name + The name of the anchor to apply. If not set, `${__object_id}` is used. + This type requires `/etc/pf.d/${anchor_name}` to exist on + `$__target_host`. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Copy anchor file to ${__target_host} + __file "/etc/pf.d/80_dns" --source - < +Kamila Součková +Jake Guffey + + +COPYING +------- +Copyright \(C) 2020 Evilham. +Copyright \(C) 2016 Kamila Součková. +Copyright \(C) 2012 Jake Guffey. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__pf_apply_anchor/parameter/optional b/cdist/conf/type/__pf_apply_anchor/parameter/optional new file mode 100644 index 00000000..b9f61e28 --- /dev/null +++ b/cdist/conf/type/__pf_apply_anchor/parameter/optional @@ -0,0 +1 @@ +anchor_name diff --git a/cdist/conf/type/__pf_ruleset/explorer/rcvar b/cdist/conf/type/__pf_ruleset/explorer/rcvar index 20e9dfcc..7c8d535f 100755 --- a/cdist/conf/type/__pf_ruleset/explorer/rcvar +++ b/cdist/conf/type/__pf_ruleset/explorer/rcvar @@ -29,7 +29,7 @@ RC="/etc/rc.conf" PFCONF="$(grep '^pf_rules=' ${RC} | cut -d= -f2 | sed 's/"//g')" -echo ${PFCONF:-"/etc/pf.conf"} +echo "${PFCONF:-"/etc/pf.conf"}" # Debug #set +x diff --git a/cdist/conf/type/__pf_ruleset/gencode-local b/cdist/conf/type/__pf_ruleset/gencode-local deleted file mode 100755 index b4bded98..00000000 --- a/cdist/conf/type/__pf_ruleset/gencode-local +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/sh -e -# -# 2012 Jake Guffey (jake.guffey at eprotex.com) -# -# This file is part of cdist. -# -# cdist is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# cdist is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with cdist. If not, see . -# -# -# Manage pf(4) on *BSD -# - -# Debug -#exec >&2 -#set -x - -# Send files to $__target_host via $__remote_copy - -uname=$(uname) # Need to know what the cdist host is running so we know how to compute the ruleset's checksum -state=$(cat "$__object/parameter/state") - -if [ "$state" = "absent" ]; then # There is nothing more for a *local* script to do - exit 0 -fi - -if [ -f "$__object/parameter/source" ]; then - source=$(cat "$__object/parameter/source") -fi - -rcvar=$(cat "$__object/explorer/rcvar") -cksum=$(cat "$__object/explorer/cksum") - - -cat <&2 - exit 1 - ;; -esac - -# IPv6 fix -if $(echo "${__target_host}" | grep -q -E '^[0-9a-fA-F:]+$') -then - my_target_host="[${__target_host}]" -else - my_target_host="${__target_host}" -fi - -if [ -n "${cksum}" ]; then - if [ ! "\${currentSum}" = "${cksum}" ]; then - $__remote_copy "${source}" "${my_target_host}:${rcvar}.new" - fi -else # File just doesn't exist yet - $__remote_copy "${source}" "${my_target_host}:${rcvar}.new" -fi -EOF - -# Debug -#exec +x - diff --git a/cdist/conf/type/__pf_ruleset/man.rst b/cdist/conf/type/__pf_ruleset/man.rst index 5719e94e..db8873ac 100644 --- a/cdist/conf/type/__pf_ruleset/man.rst +++ b/cdist/conf/type/__pf_ruleset/man.rst @@ -10,6 +10,9 @@ DESCRIPTION ----------- This type is used on \*BSD systems to manage the pf firewall's ruleset. +It will also enable and disable the pf firewall as requested in the `state` +parameter. + REQUIRED PARAMETERS ------------------- @@ -20,9 +23,8 @@ state OPTIONAL PARAMETERS ------------------- source - If supplied, use to define the ruleset to load onto the $__target_host for pf(4). - Note that this type is almost useless without a ruleset defined, but it's technically not - needed, e.g. for the case of disabling the firewall temporarily. + Required when state is "present". + Defines the ruleset to load onto the $__target_host for `pf(4)`. EXAMPLES @@ -30,10 +32,10 @@ EXAMPLES .. code-block:: sh - # Remove the current ruleset in place + # Remove the current ruleset in place and disable pf __pf_ruleset --state absent - # Enable the firewall with the ruleset defined in $__manifest/files/pf.conf + # Enable pf with the ruleset defined in $__manifest/files/pf.conf __pf_ruleset --state present --source $__manifest/files/pf.conf @@ -44,11 +46,13 @@ SEE ALSO AUTHORS ------- +Kamila Součková Jake Guffey COPYING ------- +Copyright \(C) 2016 Kamila Součková. Copyright \(C) 2012 Jake Guffey. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the diff --git a/cdist/conf/type/__pf_ruleset/manifest b/cdist/conf/type/__pf_ruleset/manifest new file mode 100755 index 00000000..27b35328 --- /dev/null +++ b/cdist/conf/type/__pf_ruleset/manifest @@ -0,0 +1,46 @@ +#!/bin/sh -e +# +# 2016 Kamila Součková (coding at kamila.is) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Manage pf(4) on *BSD +# + +rcvar="$(cat "${__object}/explorer/rcvar")" +state="$(cat "${__object}/parameter/state")" +if [ -f "${__object}/parameter/source" ]; then + source="$(cat "${__object}/parameter/source")" +fi + +if [ "${state}" = "absent" ]; then + action="/etc/rc.d/pf stop" +else + action="/etc/rc.d/pf reload || /etc/rc.d/pf start" +fi + +__key_value __pf_ruleset/rcvar \ + --state "${state}" \ + --file /etc/rc.conf \ + --delimiter "=" \ + --key "pf_enable" \ + --value "YES" + +require="__key_value/__pf_ruleset/rcvar" __config_file "${rcvar}" \ + --source "${source}" \ + --state "${state}" \ + --onchange "${action}" diff --git a/cdist/conf/type/__ping/gencode-remote b/cdist/conf/type/__ping/gencode-remote new file mode 100644 index 00000000..1341b954 --- /dev/null +++ b/cdist/conf/type/__ping/gencode-remote @@ -0,0 +1,12 @@ +#!/bin/sh -e +# +# Copyright (C) 2018 Olliver Schinagl +# +# SPDX-License-Identifier: GPL-3.0+ +# + +set -eu + +echo "echo 'pong'" + +exit 0 diff --git a/cdist/conf/type/__ping/man.rst b/cdist/conf/type/__ping/man.rst new file mode 100644 index 00000000..e08643dc --- /dev/null +++ b/cdist/conf/type/__ping/man.rst @@ -0,0 +1,43 @@ +cdist-type__ping(7) +================================== + +NAME +---- +cdist-type__ping - Try to connect to host and return 'pong' on success + + +DESCRIPTION +----------- +A simple type which tries to connect to a remote host and runs a simple command +to ensure everything is working. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + __ping + + +AUTHORS +------- +Olliver Schinagl + + +COPYING +------- +Copyright \(C) 2018 Schinagl. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__ping/singleton b/cdist/conf/type/__ping/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__postfix/manifest b/cdist/conf/type/__postfix/manifest index 1aea53a1..121bba96 100755 --- a/cdist/conf/type/__postfix/manifest +++ b/cdist/conf/type/__postfix/manifest @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2012-2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # @@ -18,16 +19,4 @@ # along with cdist. If not, see . # - -os=$(cat "$__global/explorer/os") - -case "$os" in - ubuntu|debian|archlinux|suse|scientific|centos|devuan) - __package postfix --state present - ;; - *) - echo "Your operating system ($os) is currently not supported by this type (${__type##*/})." >&2 - echo "Please contribute an implementation for it if you can." >&2 - exit 1 - ;; -esac +__package postfix --state present diff --git a/cdist/conf/type/__postfix_master/gencode-remote b/cdist/conf/type/__postfix_master/gencode-remote index 7c109a69..73de1088 100755 --- a/cdist/conf/type/__postfix_master/gencode-remote +++ b/cdist/conf/type/__postfix_master/gencode-remote @@ -67,7 +67,7 @@ case "$state_should" in remove_entry fi cat << DONE -cat >> "$config" << ${__type##*/}_DONE +cat >> "$config" << "${__type##*/}_DONE" $(cat "$entry") ${__type##*/}_DONE DONE diff --git a/cdist/conf/type/__postfix_master/manifest b/cdist/conf/type/__postfix_master/manifest index 4991a13d..0960ea41 100755 --- a/cdist/conf/type/__postfix_master/manifest +++ b/cdist/conf/type/__postfix_master/manifest @@ -36,7 +36,6 @@ __postfix # Default to object_id service="$(cat "$__object/parameter/service" 2>/dev/null || echo "$__object_id")" -state="$(cat "$__object/parameter/state")" # NOTE: keep variables in sync in manifest,explorer,gencode-* prefix="#cdist:$__object_name" @@ -51,7 +50,6 @@ entry="$__object/files/entry" echo "# $(cat "$__object/parameter/comment")" fi printf "%s " "$service" - printf "%s " "$type" for parameter in type private unpriv chroot wakeup maxproc; do printf "%s " "$(cat "$__object/parameter/$parameter")" done diff --git a/cdist/conf/type/__postfix_master/parameter/optional b/cdist/conf/type/__postfix_master/parameter/optional index 792b42c5..410482b8 100644 --- a/cdist/conf/type/__postfix_master/parameter/optional +++ b/cdist/conf/type/__postfix_master/parameter/optional @@ -4,6 +4,5 @@ unpriv chroot wakeup maxproc -option comment state diff --git a/cdist/conf/type/__postfix_master/parameter/optional_multiple b/cdist/conf/type/__postfix_master/parameter/optional_multiple new file mode 100644 index 00000000..01925a15 --- /dev/null +++ b/cdist/conf/type/__postfix_master/parameter/optional_multiple @@ -0,0 +1 @@ +option diff --git a/cdist/conf/type/__postfix_postconf/explorer/value b/cdist/conf/type/__postfix_postconf/explorer/value index 17126c94..67dacad8 100755 --- a/cdist/conf/type/__postfix_postconf/explorer/value +++ b/cdist/conf/type/__postfix_postconf/explorer/value @@ -22,7 +22,7 @@ os=$("$__explorer/os") case "$os" in - ubuntu|debian|archlinux|suse|scientific|centos|devuan) + alpine|ubuntu|debian|archlinux|suse|scientific|centos|devuan) : ;; *) diff --git a/cdist/conf/type/__postfix_postconf/gencode-remote b/cdist/conf/type/__postfix_postconf/gencode-remote index 6df0da7f..279dddd4 100755 --- a/cdist/conf/type/__postfix_postconf/gencode-remote +++ b/cdist/conf/type/__postfix_postconf/gencode-remote @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2012-2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # @@ -21,7 +22,7 @@ os=$(cat "$__global/explorer/os") case "$os" in - ubuntu|debian|archlinux|suse|scientific|centos|devuan) + alpine|archlinux|centos|debian|devuan|suse|scientific|ubuntu) : ;; *) diff --git a/cdist/conf/type/__postgres_database/explorer/state b/cdist/conf/type/__postgres_database/explorer/state index dc9659e2..d68d4120 100755 --- a/cdist/conf/type/__postgres_database/explorer/state +++ b/cdist/conf/type/__postgres_database/explorer/state @@ -18,10 +18,25 @@ # along with cdist. If not, see . # +case "$("${__explorer}/os")" +in + netbsd) + postgres_user='pgsql' + ;; + openbsd) + postgres_user='_postgresql' + ;; + *) + postgres_user='postgres' + ;; +esac + + name="$__object_id" -if su - postgres -c "echo '\q' | psql '$name'" 2>/dev/null; then - echo "present" +if test -n "$(su - "$postgres_user" -c "psql postgres -twAc \"SELECT 1 FROM pg_database WHERE datname='$name'\"")" +then + echo 'present' else - echo "absent" + echo 'absent' fi diff --git a/cdist/conf/type/__postgres_database/gencode-remote b/cdist/conf/type/__postgres_database/gencode-remote index 92301fb8..0f11cff4 100755 --- a/cdist/conf/type/__postgres_database/gencode-remote +++ b/cdist/conf/type/__postgres_database/gencode-remote @@ -18,6 +18,20 @@ # along with cdist. If not, see . # +case "$(cat "${__global}/explorer/os")" +in + netbsd) + postgres_user='pgsql' + ;; + openbsd) + postgres_user='_postgresql' + ;; + *) + postgres_user='postgres' + ;; +esac + + name="$__object_id" state_should="$(cat "$__object/parameter/state")" state_is="$(cat "$__object/explorer/state")" @@ -27,12 +41,37 @@ if [ "$state_should" != "$state_is" ]; then present) owner="" if [ -f "$__object/parameter/owner" ]; then - owner="-O '$(cat "$__object/parameter/owner")'" + owner="-O \"$(cat "$__object/parameter/owner")\"" fi - echo "su - postgres -c \"createdb $owner '$name'\"" + + template="" + if [ -f "$__object/parameter/template" ]; then + template="--template \"$(cat "$__object/parameter/template")\"" + fi + + encoding="" + if [ -f "$__object/parameter/encoding" ]; then + encoding="--encoding \"$(cat "$__object/parameter/encoding")\"" + fi + + lc_collate="" + if [ -f "$__object/parameter/lc-collate" ]; then + lc_collate="--lc-collate \"$(cat "$__object/parameter/lc-collate")\"" + fi + + lc_ctype="" + if [ -f "$__object/parameter/lc-ctype" ]; then + lc_ctype="--lc-ctype \"$(cat "$__object/parameter/lc-ctype")\"" + fi + + cat << EOF +su - '$postgres_user' -c "createdb $owner \"$name\" $template $encoding $lc_collate $lc_ctype" +EOF ;; absent) - echo "su - postgres -c \"dropdb '$name'\"" + cat << EOF +su - '$postgres_user' -c "dropdb \"$name\"" +EOF ;; esac fi diff --git a/cdist/conf/type/__postgres_database/man.rst b/cdist/conf/type/__postgres_database/man.rst index acceec9b..870b4917 100644 --- a/cdist/conf/type/__postgres_database/man.rst +++ b/cdist/conf/type/__postgres_database/man.rst @@ -14,10 +14,22 @@ This cdist type allows you to create or drop postgres databases. OPTIONAL PARAMETERS ------------------- state - either 'present' or 'absent', defaults to 'present'. + Either 'present' or 'absent', defaults to 'present'. owner - the role owning this database + Specifies the database user who will own the new database. + +encoding + Specifies the character encoding scheme to be used in this database. + +lc-collate + Specifies the LC_COLLATE setting to be used in this database. + +lc-ctype + Specifies the LC_CTYPE setting to be used in this database. + +template + Specifies the template database from which to build this database. EXAMPLES diff --git a/cdist/conf/type/__postgres_database/parameter/optional b/cdist/conf/type/__postgres_database/parameter/optional index d86b6469..877fbf32 100644 --- a/cdist/conf/type/__postgres_database/parameter/optional +++ b/cdist/conf/type/__postgres_database/parameter/optional @@ -1,2 +1,6 @@ state owner +encoding +lc-collate +lc-ctype +template diff --git a/cdist/conf/type/__postgres_extension/gencode-remote b/cdist/conf/type/__postgres_extension/gencode-remote index f7895103..af9c97f1 100755 --- a/cdist/conf/type/__postgres_extension/gencode-remote +++ b/cdist/conf/type/__postgres_extension/gencode-remote @@ -22,6 +22,20 @@ # along with cdist. If not, see . # +case "$(cat "${__global}/explorer/os")" +in + netbsd) + postgres_user='pgsql' + ;; + openbsd) + postgres_user='_postgresql' + ;; + *) + postgres_user='postgres' + ;; +esac + + dbname=$( echo "$__object_id" | cut -d":" -f1 ) extension=$( echo "$__object_id" | cut -d":" -f2 ) @@ -30,10 +44,10 @@ state_should=$( cat "$__object/parameter/state" ) case "$state_should" in present) cmd="CREATE EXTENSION IF NOT EXISTS $extension" - echo "su - postgres -c 'psql -c \"$cmd\" \"$dbname\"'" + echo "su - '$postgres_user' -c 'psql -c \"$cmd\" \"$dbname\"'" ;; absent) - cmd="DROP EXTENSION IF EXISTS $extenstion" - echo "su - postgres -c 'psql -c \"$cmd\" \"$dbname\"'" + cmd="DROP EXTENSION IF EXISTS $extension" + echo "su - '$postgres_user' -c 'psql -c \"$cmd\" \"$dbname\"'" ;; esac diff --git a/cdist/conf/type/__postgres_role/explorer/state b/cdist/conf/type/__postgres_role/explorer/state index 8c102df9..c8e1fa9d 100755 --- a/cdist/conf/type/__postgres_role/explorer/state +++ b/cdist/conf/type/__postgres_role/explorer/state @@ -18,10 +18,25 @@ # along with cdist. If not, see . # +case "$("${__explorer}/os")" +in + netbsd) + postgres_user='pgsql' + ;; + openbsd) + postgres_user='_postgresql' + ;; + *) + postgres_user='postgres' + ;; +esac + + name="$__object_id" -if su - postgres -c "psql -c '\du' | grep -q '^ *$name *|'"; then - echo "present" +if test -n "$(su - "$postgres_user" -c "psql postgres -twAc \"SELECT 1 FROM pg_roles WHERE rolname='$name'\"")" +then + echo 'present' else - echo "absent" + echo 'absent' fi diff --git a/cdist/conf/type/__postgres_role/gencode-remote b/cdist/conf/type/__postgres_role/gencode-remote index 14240992..282294c9 100755 --- a/cdist/conf/type/__postgres_role/gencode-remote +++ b/cdist/conf/type/__postgres_role/gencode-remote @@ -18,6 +18,20 @@ # along with cdist. If not, see . # +case "$(cat "${__global}/explorer/os")" +in + netbsd) + postgres_user='pgsql' + ;; + openbsd) + postgres_user='_postgresql' + ;; + *) + postgres_user='postgres' + ;; +esac + + name="$__object_id" state_is="$(cat "$__object/explorer/state")" state_should="$(cat "$__object/parameter/state")" @@ -34,16 +48,18 @@ case "$state_should" in if [ ! -f "$__object/parameter/$boolean" ]; then boolean="no${boolean}" fi - upper=$(echo $boolean | tr '[a-z]' '[A-Z]') + upper=$(echo $boolean | tr '[:lower:]' '[:upper:]') booleans="$booleans $upper" done - [ -n "$password" ] && password="PASSWORD '$password'" - - cmd="CREATE ROLE $name WITH $password $booleans" - echo "su - postgres -c \"psql -c \\\"$cmd\\\"\"" + [ -n "$password" ] && password="PASSWORD '$password'" + cat << EOF +su - '$postgres_user' -c "psql postgres -wc \"CREATE ROLE \\\\\"$name\\\\\" WITH $password $booleans;\"" +EOF ;; absent) - echo "su - postgres -c \"dropuser \\\"$name\\\"\"" + cat << EOF +su - '$postgres_user' -c "dropuser \"$name\"" +EOF ;; esac diff --git a/cdist/conf/type/__process/gencode-remote b/cdist/conf/type/__process/gencode-remote index fc491321..ec9691b9 100755 --- a/cdist/conf/type/__process/gencode-remote +++ b/cdist/conf/type/__process/gencode-remote @@ -52,7 +52,7 @@ case "$state_should" in if [ -f "$__object/parameter/stop" ]; then cat "$__object/parameter/stop" else - echo kill "${runs}" + echo kill "$(cat "$__object/parameter/runs")" fi echo "stopped" >> "$__messages_out" ;; diff --git a/cdist/conf/type/__prometheus_alertmanager/manifest b/cdist/conf/type/__prometheus_alertmanager/manifest index be50b71e..cf410c44 100755 --- a/cdist/conf/type/__prometheus_alertmanager/manifest +++ b/cdist/conf/type/__prometheus_alertmanager/manifest @@ -15,6 +15,7 @@ storage_path="$(cat "$__object/parameter/storage-path")" ##### INSTALL THE PACKAGE ################################################### require_pkg="" # what to require if I want to require "the package" +require="" if [ -f "$__object/parameter/install-from-backports" ]; then os=$(cat "$__global/explorer/os") os_version=$(cat "$__global/explorer/os_version") @@ -29,6 +30,7 @@ if [ -f "$__object/parameter/install-from-backports" ]; then *) echo "--install-from-backports is only supported on Devuan -- ignoring." >&2 echo "Send a pull request if you require it." >&2 + exit 1 ;; esac else @@ -48,7 +50,7 @@ __key_value alertmanager_fix_init_script --file /etc/init.d/prometheus-alertmana ##### CONFIGURE ############################################################# -FLAGS="--storage.path $storage_path --data.retention $(($retention_days*24))h --web.listen-address [::]:9093" +FLAGS="--storage.path $storage_path --data.retention $((retention_days*24))h --web.listen-address [::]:9093 --cluster.advertise-address [::]:9093" require="$require $require_pkg" \ __key_value alertmanager_args --file /etc/default/prometheus-alertmanager \ @@ -57,7 +59,7 @@ __key_value alertmanager_args --file /etc/default/prometheus-alertmanager \ require="$require __directory/$storage_path $require_pkg" \ __config_file $CONF \ - --source $config \ + --source "$config" \ --group prometheus --mode 640 \ - --onchange "service prometheus-alertmanager reload" # TODO when a config-check tool is available, check config here + --onchange "service prometheus-alertmanager restart" # TODO when a config-check tool is available, check config here diff --git a/cdist/conf/type/__prometheus_exporter/manifest b/cdist/conf/type/__prometheus_exporter/manifest index ae4ed94a..f3930ac6 100644 --- a/cdist/conf/type/__prometheus_exporter/manifest +++ b/cdist/conf/type/__prometheus_exporter/manifest @@ -2,11 +2,14 @@ export GOBIN=/opt/gocode/bin # where to find go binaries -exporter="$(cat $__object/parameter/exporter)" +exporter="$(cat "$__object/parameter/exporter")" [ -z "$exporter" ] && exporter="$__object_id" -__user prometheus --system +__user prometheus +require="__user/prometheus" __group prometheus +require="__group/prometheus" __user_groups prometheus --group prometheus +require="__user_groups/prometheus" case $exporter in node) TEXTFILES=/service/node-exporter/textfiles # path for the textfiles collector @@ -18,7 +21,7 @@ case $exporter in ;; blackbox) require="$require __daemontools_service/${exporter}-exporter __user/prometheus" __config_file "/service/${exporter}-exporter/blackbox.yml" \ - --source $__type/files/blackbox.yml \ + --source "$__type/files/blackbox.yml" \ --group prometheus --mode 640 \ --onchange "svc -h /service/${exporter}-exporter" require="$require __golang_from_vendor" __go_get github.com/prometheus/blackbox_exporter @@ -39,9 +42,9 @@ case $exporter in ;; esac -require="$require __daemontools" __daemontools_service ${exporter}-exporter --run "$run" +require="$require __daemontools" __daemontools_service "${exporter}-exporter" --run "$run" if [ -f "$__object/parameter/add-consul-service" ]; then - __consul_service ${exporter}-exporter --port $port --check-http "http://localhost:$port/metrics" --check-interval 10s + __consul_service "${exporter}-exporter" --port "$port" --check-http "http://localhost:$port/metrics" --check-interval 10s fi #__daemontools --install-init-script diff --git a/cdist/conf/type/__prometheus_server/manifest b/cdist/conf/type/__prometheus_server/manifest index 84ba53cf..9756169e 100755 --- a/cdist/conf/type/__prometheus_server/manifest +++ b/cdist/conf/type/__prometheus_server/manifest @@ -13,11 +13,12 @@ storage_path="$(cat "$__object/parameter/storage-path")" rule_files="$(cat "$__object/parameter/rule-files")" # explorer in kB => convert; by default we go with 1/2 RAM -[ "$target_heap_size" = "auto" ] && target_heap_size=$(($(cat $__global/explorer/memory)*1024/2)) +[ "$target_heap_size" = "auto" ] && target_heap_size=$(($(cat "$__global/explorer/memory")*1024/2)) ##### INSTALL THE PACKAGE ################################################### require_pkg="" # what to require if I want to require "the package" +require="" if [ -f "$__object/parameter/install-from-backports" ]; then os=$(cat "$__global/explorer/os") os_version=$(cat "$__global/explorer/os_version") @@ -32,11 +33,13 @@ if [ -f "$__object/parameter/install-from-backports" ]; then *) echo "--install-from-backports is only supported on Devuan -- ignoring." >&2 echo "Send a pull request if you require it." >&2 + exit 1 ;; esac else __package prometheus - require_pkg="__package/prometheus" + __package prometheus-blackbox-exporter + require_pkg="__package/prometheus __package/prometheus-blackbox-exporter" fi ##### PREPARE PATHS AND SUCH ################################################ @@ -45,7 +48,7 @@ require="$require $require_pkg" __directory "$storage_path" --owner prometheus - ##### CONFIGURE ############################################################# -FLAGS="--storage.tsdb.path $storage_path --storage.tsdb.retention $(($retention_days*24))h --web.listen-address [::]:9090" +FLAGS="--storage.tsdb.path $storage_path --storage.tsdb.retention $((retention_days*24))h --web.listen-address [::]:9090" # TODO it would be neat to restart prometheus on change -- __key_value really should have an --onchange parameter require="$require $require_pkg" \ @@ -55,16 +58,16 @@ __key_value prometheus_args --file /etc/default/prometheus \ require="$require __directory/$storage_path $require_pkg" \ __config_file $CONF \ - --source $config \ + --source "$config" \ --group prometheus --mode 640 \ - --onchange "promtool check config $CONF && service prometheus reload" + --onchange "promtool check config $CONF && service prometheus restart" for file in $rule_files; do - dest=$CONF_DIR/$(basename $file) + dest=$CONF_DIR/$(basename "$file") require="$require $require_pkg" \ __config_file "$dest" \ --source "$file" \ --owner prometheus \ - --onchange "promtool check rules '$dest' && service prometheus reload" + --onchange "promtool check rules '$dest' && service prometheus restart" done diff --git a/cdist/conf/type/__pyvenv/explorer/group b/cdist/conf/type/__pyvenv/explorer/group index ff072c5e..a655bda7 100755 --- a/cdist/conf/type/__pyvenv/explorer/group +++ b/cdist/conf/type/__pyvenv/explorer/group @@ -2,4 +2,4 @@ destination="/$__object_id" -stat --print "%G" ${destination} 2>/dev/null || exit 0 +stat --print "%G" "${destination}" 2>/dev/null || exit 0 diff --git a/cdist/conf/type/__pyvenv/explorer/owner b/cdist/conf/type/__pyvenv/explorer/owner index b77e3c6e..8b3c7f8e 100755 --- a/cdist/conf/type/__pyvenv/explorer/owner +++ b/cdist/conf/type/__pyvenv/explorer/owner @@ -2,4 +2,4 @@ destination="/$__object_id" -stat --print "%U" ${destination} 2>/dev/null || exit 0 +stat --print "%U" "${destination}" 2>/dev/null || exit 0 diff --git a/cdist/conf/type/__pyvenv/gencode-remote b/cdist/conf/type/__pyvenv/gencode-remote index a4f078c5..c5b64eff 100755 --- a/cdist/conf/type/__pyvenv/gencode-remote +++ b/cdist/conf/type/__pyvenv/gencode-remote @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2016 Darko Poljak (darko.poljak at gmail.com) +# 2020 Nico Schotetlius (nico.schottelius at ungleich.ch) # # This file is part of cdist. # @@ -29,28 +30,38 @@ owner="$(cat "$__object/parameter/owner")" group="$(cat "$__object/parameter/group")" mode="$(cat "$__object/parameter/mode")" -[ "$state_should" = "$state_is" -a \ - "$owner" = "$owner_is" -a \ - "$group" = "$group_is" -a \ - -n "$mode" ] && exit 0 +[ "$state_should" = "$state_is" ] && \ +[ "$owner" = "$owner_is" ] && \ +[ "$group" = "$group_is" ] && \ +[ -n "$mode" ] && exit 0 destination="/$__object_id" venvparams="$(cat "$__object/parameter/venvparams")" pyvenvparam="$__object/parameter/pyvenv" + +os=$(cat "$__global/explorer/os") + if [ -f "$pyvenvparam" ] then pyvenv=$(cat "$pyvenvparam") else - pyvenv="pyvenv" + case "$os" in + alpine|ubuntu) # no pyvenv on alpine - I assume others will follow + pyvenv="python3 -m venv" + ;; + *) + pyvenv="pyvenv" + ;; + esac fi case $state_should in present) if [ "$state_should" != "$state_is" ]; then - echo $pyvenv $venvparams "$destination" + echo "$pyvenv $venvparams $destination" fi - if [ \( -n "$owner" -a "$owner_is" != "$owner" \) -o \ - \( -n "$group" -a "$group_is" != "$group" \) ]; then + if { [ -n "$owner" ] && [ "$owner_is" != "$owner" ]; } || \ + { [ -n "$group" ] && [ "$group_is" != "$group" ]; }; then echo chown -R "${owner}:${group}" "$destination" fi if [ -n "$mode" ]; then diff --git a/cdist/conf/type/__pyvenv/man.rst b/cdist/conf/type/__pyvenv/man.rst index d7de92fa..8085ff12 100644 --- a/cdist/conf/type/__pyvenv/man.rst +++ b/cdist/conf/type/__pyvenv/man.rst @@ -9,7 +9,7 @@ cdist-type__pyvenv - Create or remove python virtual environment DESCRIPTION ----------- This cdist type allows you to create or remove python virtual -environment using pyvenv. +environment using pyvenv on python3 -m venv. It assumes pyvenv is already installed. Concrete package depends on concrete OS and/or OS version/distribution. Ensure this for e.g. in your init manifest as in the following example: @@ -57,7 +57,7 @@ EXAMPLES __pyvenv /home/services/djangoenv - # Use specific pyvenv + # Use specific pyvenv __pyvenv /home/foo/fooenv --pyvenv /usr/local/bin/pyvenv-3.4 # Create python virtualenv for user foo. @@ -76,4 +76,3 @@ COPYING ------- Copyright \(C) 2016 Darko Poljak. Free use of this software is granted under the terms of the GNU General Public License v3 or later (GPLv3+). - diff --git a/cdist/conf/type/__qemu_img/gencode-remote b/cdist/conf/type/__qemu_img/gencode-remote index 9127e5ef..94816f58 100755 --- a/cdist/conf/type/__qemu_img/gencode-remote +++ b/cdist/conf/type/__qemu_img/gencode-remote @@ -18,4 +18,4 @@ format="$(cat "$__object/parameter/format")" size="$(cat "$__object/parameter/size")" diskimage="/$__object_id" -echo qemu-img create -f \"$format\" \"$diskimage\" \"$size\" +echo "qemu-img create -f '$format' '$diskimage' '$size'" diff --git a/cdist/conf/type/__qemu_img/manifest b/cdist/conf/type/__qemu_img/manifest index e7417389..55f3bf16 100755 --- a/cdist/conf/type/__qemu_img/manifest +++ b/cdist/conf/type/__qemu_img/manifest @@ -4,7 +4,6 @@ # Default settings # -format="$(cat "$__object/parameter/format")" state_should="$(cat "$__object/parameter/state")" diskimage="/$__object_id" diff --git a/cdist/conf/type/__rsync/gencode-local b/cdist/conf/type/__rsync/gencode-local index 155f3a3a..e36ded2f 100755 --- a/cdist/conf/type/__rsync/gencode-local +++ b/cdist/conf/type/__rsync/gencode-local @@ -29,9 +29,9 @@ fi set -- if [ -f "$__object/parameter/rsync-opts" ]; then - while read opts; do + while read -r opts; do set -- "$@" "--$opts" - done < $__object/parameter/rsync-opts + done < "$__object/parameter/rsync-opts" fi echo rsync -a \ diff --git a/cdist/conf/type/__rvm/explorer/state b/cdist/conf/type/__rvm/explorer/state index f43f5509..74d17048 100755 --- a/cdist/conf/type/__rvm/explorer/state +++ b/cdist/conf/type/__rvm/explorer/state @@ -28,7 +28,7 @@ if [ "$user" = "root" ]; then echo absent fi else - if su - $user -c "[ -d \"\$HOME/.rvm\" ]" ; then + if su - "$user" -c "[ -d \"\$HOME/.rvm\" ]" ; then echo "present" else echo "absent" diff --git a/cdist/conf/type/__rvm/gencode-remote b/cdist/conf/type/__rvm/gencode-remote index 494c8fd8..993191c1 100755 --- a/cdist/conf/type/__rvm/gencode-remote +++ b/cdist/conf/type/__rvm/gencode-remote @@ -34,7 +34,7 @@ DONE absent) cat << DONE su - $user -c "rm -Rf \"\\\$HOME/.rvm\"; -sed '/rvm\/scripts\/rvm/d' \"\\\$HOME/.bashrc\" > \"\\\$HOME/.bashrc.cdist-tmp\" +sed '/rvm\\/scripts\\/rvm/d' \"\\\$HOME/.bashrc\" > \"\\\$HOME/.bashrc.cdist-tmp\" mv \"\\\$HOME/.bashrc.cdist-tmp\" \"\\\$HOME/.bashrc\"" DONE ;; diff --git a/cdist/conf/type/__rvm_gem/gencode-remote b/cdist/conf/type/__rvm_gem/gencode-remote index 1fe6e78e..9212de91 100755 --- a/cdist/conf/type/__rvm_gem/gencode-remote +++ b/cdist/conf/type/__rvm_gem/gencode-remote @@ -20,8 +20,6 @@ gem="$__object_id" gemset="$(cat "$__object/parameter/gemset")" -ruby="$(echo "$gemset" | cut -d '@' -f 1)" -gemsetname="$(echo "$gemset" | cut -d '@' -f 2)" state_is="$(cat "$__object/explorer/state")" user="$(cat "$__object/parameter/user")" state_should="$(cat "$__object/parameter/state")" diff --git a/cdist/conf/type/__rvm_gemset/explorer/state b/cdist/conf/type/__rvm_gemset/explorer/state index fa643a6e..e300453b 100755 --- a/cdist/conf/type/__rvm_gemset/explorer/state +++ b/cdist/conf/type/__rvm_gemset/explorer/state @@ -18,9 +18,6 @@ # along with cdist. If not, see . # -gemset="$__object_id" -ruby="$(echo "$gemset" | cut -d '@' -f 1)" -gemsetname="$(echo "$gemset" | cut -d '@' -f2)" user="$(cat "$__object/parameter/user")" if [ ! -e "~$user/.rvm/scripts/rvm" ] ; then @@ -28,7 +25,9 @@ if [ ! -e "~$user/.rvm/scripts/rvm" ] ; then exit 0 fi +# shellcheck disable=SC2016 if su - "$user" -c 'source ~/.rvm/scripts/rvm; rvm list strings | grep -q "^$ruby\$"'; then + # shellcheck disable=SC2016 if su - "$user" -c 'source ~/.rvm/scripts/rvm; rvm use "$ruby" > /dev/null; rvm gemset list strings | cut -f 1 -d " " | grep -q "^$gemsetname\$"'; then echo "present" exit 0 diff --git a/cdist/conf/type/__rvm_gemset/gencode-remote b/cdist/conf/type/__rvm_gemset/gencode-remote index 78851f9a..3cdc66a6 100755 --- a/cdist/conf/type/__rvm_gemset/gencode-remote +++ b/cdist/conf/type/__rvm_gemset/gencode-remote @@ -33,7 +33,7 @@ case "$state_should" in cat << DONE su - "$user" -c "source ~/.rvm/scripts/rvm; rvm $gemset --create" DONE - if -f "$__object/parameter/default"; then + if [ -f "$__object/parameter/default" ]; then cat << DONE su - "$user" -c "source ~/.rvm/scripts/rvm; rvm use --default $gemset" DONE diff --git a/cdist/conf/type/__rvm_ruby/gencode-remote b/cdist/conf/type/__rvm_ruby/gencode-remote index 9bbc6031..f2fd41ef 100755 --- a/cdist/conf/type/__rvm_ruby/gencode-remote +++ b/cdist/conf/type/__rvm_ruby/gencode-remote @@ -21,7 +21,6 @@ ruby="$__object_id" state_is="$(cat "$__object/explorer/state")" user="$(cat "$__object/parameter/user")" -default="$(cat "$__object/parameter/default" 2>/dev/null || true)" state_should="$(cat "$__object/parameter/state")" [ "$state_is" = "$state_should" ] && exit 0 diff --git a/cdist/conf/type/__sensible_editor/explorer/editor_path b/cdist/conf/type/__sensible_editor/explorer/editor_path new file mode 100644 index 00000000..dcf63c9b --- /dev/null +++ b/cdist/conf/type/__sensible_editor/explorer/editor_path @@ -0,0 +1,131 @@ +#!/bin/sh -e +# +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Check if the given editor is present on the target system and determine its +# absolute path. +# + +die() { + echo "$@" >&2 + exit 1 +} + +editor_missing() { die "Editor '$1' is missing on the target system."; } +editor_no_alternative() { + die "Editor '$1' is not in the alternatives list of the target system." \ + "$(test -n "${editors}" && printf '\nPlease choose one of:\n\n%s\n' "${editors}")" +} + +# No need to check for the path if the file is supposed to be removed. +test "$(cat "${__object}/parameter/state")" != 'absent' || exit 0 + + +case $("${__explorer}/os") +in + debian|devuan|ubuntu) + has_alternatives=true + + # NOTE: Old versions do not support `--list`, in this case ignore the errors. + # This will require an absolute path to be provided, though. + editors=$(update-alternatives --list editor 2>/dev/null) + ;; + *) + # NOTE: RedHat has an alternatives system but it doesn't usually track + # editors and it is a pain to extract the list. + has_alternatives=false + ;; +esac + +# Read --editor parameter and check its value since it is "optional" +editor=$(cat "${__object}/parameter/editor" 2>/dev/null) || true +test -n "${editor}" || die 'Please provide an --editor to configure.' + +case $editor +in + /*) + is_abspath=true + ;; + */*) + die 'Relative editor paths are not supported' + ;; + *) + is_abspath=false + ;; +esac + + +if $has_alternatives && test -n "${editors}" +then + IFS=' +' + if ! $is_abspath + then + # First, try to resolve the absolute path using $editors. + while true + do + for e in $editors + do + if test "$(basename "${e}")" = "${editor}" + then + editor="${e}" + break 2 # break out of both loops + fi + done + + # Iterating through alternatives did not yield a result + editor_no_alternative "${editor}" + break + done + fi + + # Check if editor is present + test -f "${editor}" || editor_missing "${editor}" + + for e in $editors + do + if test "${editor}" = "${e}" + then + # Editor is part of the alternatives list -> use it! + echo "${editor}" + exit 0 + fi + done + + editor_no_alternative "${editor}" +else + # NOTE: This branch is mostly for RedHat-based systems which do + # not track editor alternatives. To make this type useful + # on RedHat at all we allow an absoloute path to be provided + # in any case. + + if $is_abspath + then + test -x "${editor}" || editor_missing "${editor}" + + echo "${editor}" + exit 0 + else + die "The target doesn't list any editor alternatives. " \ + "Please specify an absolute path or populate the alternatives list." + fi +fi + +# The script should never reach this statement! +exit 1 diff --git a/cdist/conf/type/__sensible_editor/explorer/group b/cdist/conf/type/__sensible_editor/explorer/group new file mode 100644 index 00000000..5d288189 --- /dev/null +++ b/cdist/conf/type/__sensible_editor/explorer/group @@ -0,0 +1,26 @@ +#!/bin/sh -e +# +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Determines the primary group of the user. +# + +user=$__object_id + +id -gn "${user}" 2>/dev/null diff --git a/cdist/conf/type/__sensible_editor/explorer/user_home b/cdist/conf/type/__sensible_editor/explorer/user_home new file mode 100644 index 00000000..b88243f7 --- /dev/null +++ b/cdist/conf/type/__sensible_editor/explorer/user_home @@ -0,0 +1,33 @@ +#!/bin/sh -e +# +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# +# Determines the home folder of the target user. +# + +user=$__object_id +home=$(getent passwd "${user}" | cut -d':' -f6) + +if ! test -d "${home}" +then + echo "Cannot find home directory of user ${user}" >&2 + exit 1 +fi + +echo "${home}" diff --git a/cdist/conf/type/__sensible_editor/man.rst b/cdist/conf/type/__sensible_editor/man.rst new file mode 100644 index 00000000..9b805e06 --- /dev/null +++ b/cdist/conf/type/__sensible_editor/man.rst @@ -0,0 +1,78 @@ +cdist-type__sensible_editor(7) +============================== + +NAME +---- +cdist-type__sensible_editor - Select the sensible-editor + + +DESCRIPTION +----------- +This cdist type allows you to select the :strong:`sensible-editor` for +a given user. + + +REQUIRED PARAMETERS +------------------- +editor + Name or path of the editor to be selected. + On systems other than Debian derivatives an absolute path is required. + + It is permissible to omit this parameter if --state is absent. + + +OPTIONAL PARAMETERS +------------------- +state + 'present', 'absent', or 'exists'. Defaults to 'present', where: + + present + the sensible-editor is exactly what is specified in --editor. + absent + no sensible-editor configuration is present. + exists + the sensible-editor will be set to what is specified in --editor, + unless there already is a configuration on the target system. + + +EXAMPLES +-------- + +.. code-block:: sh + + __sensible_editor root --editor /bin/ed # ed(1) is the standard + __sensible_editor noob --editor nano + + +LIMITATIONS +----------- + +This type depends upon the :strong:`sensible-editor`\ (1) script which +is part of the sensible-utils package. + +Therefore, the following operating systems are supported: + * Debian 8 (jessie) or later + * Devuan + * Ubuntu 8.10 (intrepid) or later + * RHEL/CentOS 7 or later (EPEL repo required) + * Fedora 21 or later + +Note: on old versions of Ubuntu the sensible-* utils are part of the +debianutils package. + +SEE ALSO +-------- +:strong:`select-editor`\ (1), :strong:`sensible-editor`\ (1). + + +AUTHOR +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2019 Dennis Camera. +You can redistribute it and/or modify it under the terms of the GNU General +Public License as published by the Free Software Foundation, either version 3 of +the License, or (at your option) any later version. diff --git a/cdist/conf/type/__sensible_editor/manifest b/cdist/conf/type/__sensible_editor/manifest new file mode 100644 index 00000000..1cdb0c2c --- /dev/null +++ b/cdist/conf/type/__sensible_editor/manifest @@ -0,0 +1,94 @@ +#!/bin/sh -e +# -*- mode: sh; indent-tabs-mode: t -*- +# +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +version_ge() { + awk -F '[^0-9.]' -v target="${1:?}" ' + function max(x, y) { return x > y ? x : y; } + BEGIN { + getline; + nx = split($1, x, "."); + ny = split(target, y, "."); + for (i = 1; i <= max(nx, ny); ++i) { + diff = int(x[i]) - int(y[i]); + if (diff < 0) exit 1; + else if (diff > 0) exit 0; + else continue; + } + }' +} + +not_supported() { + echo "OS ${os} does not support __sensible_editor." >&2 + echo 'If it does, please provide a patch.' >&2 + exit 1 +} + +os=$(cat "${__global}/explorer/os") +os_version=$(cat "${__global}/explorer/os_version") + +state=$(cat "${__object}/parameter/state") +user=$__object_id + +if test "${state}" != 'present' && test "${state}" != 'exists' && test "${state}" != 'absent' +then + echo 'Only "present", "exists", and "absent" are allowed for --state' >&2 + exit 1 +fi + +package_name='sensible-utils' + +case $os +in + debian) + pkg_type='apt' + ;; + devuan) + pkg_type='apt' + ;; + ubuntu) + (echo "${os_version}" | version_ge 10.04) || package_name='debianutils' + pkg_type='apt' + ;; + centos|fedora|redhat|scientific) + pkg_type='yum' + ;; + *) + not_supported + ;; +esac + +if test "${state}" != 'absent' +then + __package "${package_name}" --state present \ + --type "${pkg_type}" + export require="__package/${package_name}" +fi + +editor_path=$(cat "${__object}/explorer/editor_path") +user_home=$(cat "${__object}/explorer/user_home") +group=$(cat "${__object}/explorer/group") + +__file "${user_home}/.selected_editor" --state "${state}" \ + --owner "${user}" --group "${group}" --mode 0644 \ + --source - < + + +COPYING +------- +Copyright \(C) 2019 Timothée Floure. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__service/manifest b/cdist/conf/type/__service/manifest new file mode 100644 index 00000000..beb0713c --- /dev/null +++ b/cdist/conf/type/__service/manifest @@ -0,0 +1,17 @@ +#!/bin/sh + +manager="$(cat "$__object/explorer/service-manager")" + +name=$__object_id +action="$(cat "$__object/parameter/action")" + +case "$manager" in + systemd) + test "$action" = "start" && action="running" + test "$action" = "stop" && action="stopped" + __systemd_service "$name" --state "$action" + ;; + *) + # Unknown: handled by `service $NAME $action` in gencode-remote. + ;; +esac diff --git a/cdist/conf/type/__service/parameter/required b/cdist/conf/type/__service/parameter/required new file mode 100644 index 00000000..a9f84d41 --- /dev/null +++ b/cdist/conf/type/__service/parameter/required @@ -0,0 +1 @@ +action diff --git a/cdist/conf/type/__ssh_authorized_key/explorer/entry b/cdist/conf/type/__ssh_authorized_key/explorer/entry index 1535d348..ccab0afc 100755 --- a/cdist/conf/type/__ssh_authorized_key/explorer/entry +++ b/cdist/conf/type/__ssh_authorized_key/explorer/entry @@ -19,13 +19,16 @@ # # extract the keytype and base64 encoded key ignoring any options and comment -type_and_key="$(cat "$__object/parameter/key" | tr ' ' '\n' | awk '/^(ssh|ecdsa)-[^ ]+/ { printf $1" "; getline; printf $1 }')" +type_and_key="$(tr ' ' '\n' < "$__object/parameter/key"| awk '/^(ssh|ecdsa)-[^ ]+/ { printf $1" "; getline; printf $1 }')" # If type_and_key is empty, which is the case with an invalid key, do not grep $file because it results # in greping everything in file and all entries from file are removed. if [ -n "${type_and_key}" ] then - file="$(cat $__object/parameter/file)" + file="$(cat "$__object/parameter/file")" # get any entries that match the type and key - grep ".*$type_and_key\([ \n]\|$\)" "$file" || true + + # NOTE: Do not match from the beginning of the line as there may be options + # preceeding the key. + grep "${type_and_key}\\([ \\n].*\\)*$" "$file" || true fi diff --git a/cdist/conf/type/__ssh_authorized_key/gencode-remote b/cdist/conf/type/__ssh_authorized_key/gencode-remote index 7ded7dc6..f37aa565 100755 --- a/cdist/conf/type/__ssh_authorized_key/gencode-remote +++ b/cdist/conf/type/__ssh_authorized_key/gencode-remote @@ -48,7 +48,7 @@ add_line() { line="$2" # escape single quotes line_sanitised=$(echo "$line" | sed -e "s/'/'\"'\"'/g") - printf '%s' "printf '%s\n' '$line_sanitised' >> $file" + printf '%s' "printf '%s\\n' '$line_sanitised' >> $file" } @@ -59,7 +59,7 @@ mkdir "$__object/files" ( if [ -f "$__object/parameter/option" ]; then # comma seperated list of options - options="$(cat "$__object/parameter/option" | tr '\n' ',')" + options="$(tr '\n' ',' < "$__object/parameter/option")" printf '%s ' "${options%*,}" fi if [ -f "$__object/parameter/comment" ]; then @@ -78,7 +78,7 @@ if [ -s "$__object/explorer/entry" ]; then # Note that the files have to be sorted for comparison with `comm`. sort "$__object/explorer/entry" > "$__object/files/is" comm -13 "$__object/files/should" "$__object/files/is" | { - while read entry; do + while read -r entry; do remove_line "$file" "$entry" done } @@ -88,7 +88,7 @@ fi entry="$(cat "$__object/files/should")" state_should="$(cat "$__object/parameter/state")" num_existing_entries=$(grep -c -F -x "$entry" "$__object/explorer/entry" || true) -if [ $num_existing_entries -eq 1 ]; then +if [ "$num_existing_entries" -eq 1 ]; then state_is="present" else # Posix grep does not define the -m option, so we can not remove a single diff --git a/cdist/conf/type/__ssh_authorized_key/man.rst b/cdist/conf/type/__ssh_authorized_key/man.rst index 087a3dae..5bae02aa 100644 --- a/cdist/conf/type/__ssh_authorized_key/man.rst +++ b/cdist/conf/type/__ssh_authorized_key/man.rst @@ -15,25 +15,27 @@ This type was created to be used by the __ssh_authorized_keys type. REQUIRED PARAMETERS ------------------- file - the authorized_keys file to which the given key should be added + The authorized_keys file where the given key should be managed. key - a string containing the ssh keytype, base 64 encoded key and optional - trailing comment which shall be added to the given authorized_keys file. + The ssh key which shall be managed in this authorized_keys file. + Must be a string containing the ssh keytype, base 64 encoded key and + optional trailing comment which shall be added to the given + authorized_keys file. OPTIONAL PARAMETERS ------------------- comment - explicit comment instead of the one which may be trailing the given key + Use this comment instead of the one which may be trailing in the key. option - an option to set for this authorized_key entry. + An option to set for this authorized_key entry. Can be specified multiple times. See sshd(8) for available options. state - if the given keys should be 'present' or 'absent', defaults to 'present'. + If the managed key should be 'present' or 'absent', defaults to 'present'. MESSAGES @@ -64,7 +66,7 @@ EXAMPLES SEE ALSO -------- -:strong:`cdist__ssh_authorized_keys`\ (7), :strong:`sshd`\ (8) +:strong:`cdist-type__ssh_authorized_keys`\ (7), :strong:`sshd`\ (8) AUTHORS diff --git a/cdist/conf/type/__ssh_authorized_keys/explorer/file b/cdist/conf/type/__ssh_authorized_keys/explorer/file index 5a02721a..017bcb38 100755 --- a/cdist/conf/type/__ssh_authorized_keys/explorer/file +++ b/cdist/conf/type/__ssh_authorized_keys/explorer/file @@ -1,6 +1,7 @@ #!/bin/sh # # 2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,9 +20,42 @@ # if [ -f "$__object/parameter/file" ]; then - cat "$__object/parameter/file" + cat "$__object/parameter/file" else - owner="$(cat "$__object/parameter/owner" 2>/dev/null || echo "$__object_id")" - home=$(getent passwd "$owner" | cut -d':' -f 6) - echo "$home/.ssh/authorized_keys" + if [ -s "$__object/parameter/owner" ] + then + owner=$(cat "$__object/parameter/owner") + else + owner="$__object_id" + fi + + if command -v getent >/dev/null + then + owner_line=$(getent passwd "$owner") + elif [ -f /etc/passwd ] + then + case $owner + in + [0-9][0-9]*) + owner_line=$(awk -F: "\$3 == \"${owner}\" { print }" /etc/passwd) + ;; + *) + owner_line=$(awk -F: "\$1 == \"${owner}\" { print }" /etc/passwd) + ;; + esac + fi + + if [ "$owner_line" ] + then + home=$(echo "$owner_line" | cut -d':' -f6) + fi + + if [ ! -d "$home" ] + then + # Don't know how to determine user's home directory, fall back to ~ + home="~$owner" + command -v realpath >/dev/null && home=$(realpath "$home") + fi + + [ -d "$home" ] && echo "$home/.ssh/authorized_keys" fi diff --git a/cdist/conf/type/__ssh_authorized_keys/explorer/group b/cdist/conf/type/__ssh_authorized_keys/explorer/group index 72a4e314..d259050f 100755 --- a/cdist/conf/type/__ssh_authorized_keys/explorer/group +++ b/cdist/conf/type/__ssh_authorized_keys/explorer/group @@ -1,6 +1,7 @@ #!/bin/sh # # 2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -18,6 +19,28 @@ # along with cdist. If not, see . # -owner="$(cat "$__object/parameter/owner" 2>/dev/null || echo "$__object_id")" -gid="$(getent passwd "$owner" | cut -d':' -f 4)" -getent group "$gid" || true +if [ -s "$__object/parameter/owner" ] +then + owner=$(cat "$__object/parameter/owner") +else + owner="$__object_id" +fi + +if command -v getent >/dev/null +then + gid=$(getent passwd "$owner" | cut -d':' -f4) + getent group "$gid" || true +else + # Fallback to local file scanning + case $owner + in + [0-9][0-9]*) + gid=$(awk -F: "\$3 == \"${owner}\" { print \$4 }" /etc/passwd) + ;; + *) + gid=$(awk -F: "\$1 == \"${owner}\" { print \$4 }" /etc/passwd) + ;; + esac + + awk -F: "\$3 == \"$gid\" { print }" /etc/group +fi diff --git a/cdist/conf/type/__ssh_authorized_keys/explorer/keys b/cdist/conf/type/__ssh_authorized_keys/explorer/keys new file mode 100755 index 00000000..cec25746 --- /dev/null +++ b/cdist/conf/type/__ssh_authorized_keys/explorer/keys @@ -0,0 +1,9 @@ +#!/bin/sh -e + +# shellcheck disable=SC1090 +file="$( . "$__type_explorer/file" )" + +if [ -f "$file" ] +then + cat "$file" +fi diff --git a/cdist/conf/type/__ssh_authorized_keys/man.rst b/cdist/conf/type/__ssh_authorized_keys/man.rst index ba310ff9..dac6adeb 100644 --- a/cdist/conf/type/__ssh_authorized_keys/man.rst +++ b/cdist/conf/type/__ssh_authorized_keys/man.rst @@ -20,42 +20,48 @@ then left to the user to ensure that the file exists and that ownership and permissions work with ssh. -REQUIRED PARAMETERS -------------------- +REQUIRED MULTIPLE PARAMETERS +---------------------------- key - the ssh key which shall be added to this authorized_keys file. - Must be a string and can be specified multiple times. + An ssh key which shall be managed in this authorized_keys file. + Must be a string containing the ssh keytype, base 64 encoded key and + optional trailing comment which shall be added to the given + authorized_keys file. + Can be specified multiple times. OPTIONAL PARAMETERS ------------------- comment - explicit comment instead of the one which may be trailing the given key + Use this comment instead of the one which may be trailing in each key. file - an alternative destination file, defaults to ~$owner/.ssh/authorized_keys + An alternative destination file, defaults to ~$owner/.ssh/authorized_keys. option - an option to set for all created authorized_key entries. + An option to set for all authorized_key entries in the key parameter. Can be specified multiple times. See sshd(8) for available options. owner - the user owning the authorized_keys file, defaults to object_id. + The user owning the authorized_keys file, defaults to object_id. state - if the given keys should be 'present' or 'absent', defaults to 'present'. + If the given keys should be 'present' or 'absent', defaults to 'present'. BOOLEAN PARAMETERS ------------------ noparent - don't create or change ownership and permissions of the directory containing - the authorized_keys file + Don't create or change ownership and permissions of the directory containing + the authorized_keys file. nofile - don't manage existence, ownership and permissions of the the authorized_keys - file + Don't manage existence, ownership and permissions of the the authorized_keys + file. + +remove-unknown + Remove undefined keys. EXAMPLES @@ -67,6 +73,12 @@ EXAMPLES __ssh_authorized_keys root \ --key "$(cat ~/.ssh/id_rsa.pub)" + # same as above, but make sure your key is only key in + # root's authorized_keys file + __ssh_authorized_keys root \ + --key "$(cat ~/.ssh/id_rsa.pub)" \ + --remove-unknown + # allow key to login as user-name __ssh_authorized_keys user-name \ --key "ssh-rsa AXYZAAB3NzaC1yc2..." diff --git a/cdist/conf/type/__ssh_authorized_keys/manifest b/cdist/conf/type/__ssh_authorized_keys/manifest index 9fad8896..b319316b 100755 --- a/cdist/conf/type/__ssh_authorized_keys/manifest +++ b/cdist/conf/type/__ssh_authorized_keys/manifest @@ -23,7 +23,13 @@ owner="$(cat "$__object/parameter/owner" 2>/dev/null || echo "$__object_id")" state="$(cat "$__object/parameter/state" 2>/dev/null)" file="$(cat "$__object/explorer/file")" -if [ ! -f "$__object/parameter/noparent" -o ! -f "$__object/parameter/nofile" ]; then +if [ ! -f "$__object/parameter/nofile" ] && [ -z "$file" ] +then + echo "Cannot determine path of authorized_keys file" >&2 + exit 1 +fi + +if [ ! -f "$__object/parameter/noparent" ] || [ ! -f "$__object/parameter/nofile" ]; then group="$(cut -d':' -f 1 "$__object/explorer/group")" if [ -z "$group" ]; then echo "Failed to get owners group from explorer." >&2 @@ -45,31 +51,24 @@ if [ ! -f "$__object/parameter/noparent" -o ! -f "$__object/parameter/nofile" ]; fi fi -# Remove legacy blocks created by old versions of this type -# FIXME: remove me in 3.2+ -__block "$__object_name" \ - --file "$file" \ - --prefix "#cdist:$__object_name" \ - --suffix "#/cdist:$__object_name" \ - --state 'absent' \ - --text - << DONE -remove legacy block -DONE -export require="__block/$__object_name" - _cksum() { echo "$1" | cksum | cut -d' ' -f 1 } -while read key; do - type_and_key="$(echo "$key" | tr ' ' '\n' | awk '/^(ssh|ecdsa)-[^ ]+/ { printf $1" "; getline; printf $1 }')" +_type_and_key() { + echo "$1" | tr ' ' '\n' | awk '/^(ssh|ecdsa)-[^ ]+/ { printf $1" "; getline; printf $1 }' +} + +while read -r key; do + type_and_key="$( _type_and_key "$key" )" object_id="$(_cksum "$file")-$(_cksum "$type_and_key")" set -- "$object_id" set -- "$@" --file "$file" set -- "$@" --key "$key" set -- "$@" --state "$state" if [ -f "$__object/parameter/option" ]; then - set -- "$@" --option "$(cat "$__object/parameter/option")" + # shellcheck disable=SC2046 + set -- "$@" $(printf -- '--option %s ' $(cat "$__object/parameter/option")) fi if [ -f "$__object/parameter/comment" ]; then set -- "$@" --comment "$(cat "$__object/parameter/comment")" @@ -77,3 +76,24 @@ while read key; do # Ensure __ssh_authorized_key does not read stdin __ssh_authorized_key "$@" < /dev/null done < "$__object/parameter/key" + +if [ -f "$__object/parameter/remove-unknown" ] && + [ -s "$__object/explorer/keys" ] +then + while read -r key + do + type_and_key="$( _type_and_key "$key" )" + + if grep -Fq "$type_and_key" "$__object/parameter/key" + then + continue + fi + + __ssh_authorized_key "remove-$( _cksum "$file$key" )" \ + --file "$file" \ + --key "$key" \ + --state absent \ + < /dev/null + done \ + < "$__object/explorer/keys" +fi diff --git a/cdist/conf/type/__ssh_authorized_keys/parameter/boolean b/cdist/conf/type/__ssh_authorized_keys/parameter/boolean index 4bb126fe..7388fed5 100644 --- a/cdist/conf/type/__ssh_authorized_keys/parameter/boolean +++ b/cdist/conf/type/__ssh_authorized_keys/parameter/boolean @@ -1,2 +1,3 @@ noparent nofile +remove-unknown diff --git a/cdist/conf/type/__ssh_authorized_keys/parameter/optional b/cdist/conf/type/__ssh_authorized_keys/parameter/optional index 21f9bc29..fa64fc43 100644 --- a/cdist/conf/type/__ssh_authorized_keys/parameter/optional +++ b/cdist/conf/type/__ssh_authorized_keys/parameter/optional @@ -1,5 +1,4 @@ comment file -option owner state diff --git a/cdist/conf/type/__ssh_authorized_keys/parameter/optional_multiple b/cdist/conf/type/__ssh_authorized_keys/parameter/optional_multiple new file mode 100644 index 00000000..01925a15 --- /dev/null +++ b/cdist/conf/type/__ssh_authorized_keys/parameter/optional_multiple @@ -0,0 +1 @@ +option diff --git a/cdist/conf/type/__ssh_dot_ssh/explorer/group b/cdist/conf/type/__ssh_dot_ssh/explorer/group index cdea6fe7..faf44cb8 100755 --- a/cdist/conf/type/__ssh_dot_ssh/explorer/group +++ b/cdist/conf/type/__ssh_dot_ssh/explorer/group @@ -1,6 +1,7 @@ #!/bin/sh # # 2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -18,5 +19,11 @@ # along with cdist. If not, see . # -gid="$("$__type_explorer/passwd" | cut -d':' -f 4)" -getent group "$gid" || true +gid=$("$__type_explorer/passwd" | cut -d':' -f4) + +if command -v getent >/dev/null +then + getent group "$gid" || true +else + awk -F: "\$3 == \"$gid\" { print }" /etc/group +fi diff --git a/cdist/conf/type/__ssh_dot_ssh/explorer/passwd b/cdist/conf/type/__ssh_dot_ssh/explorer/passwd index 3fbad06f..42686b20 100755 --- a/cdist/conf/type/__ssh_dot_ssh/explorer/passwd +++ b/cdist/conf/type/__ssh_dot_ssh/explorer/passwd @@ -2,6 +2,7 @@ # # 2012 Steven Armstrong (steven-cdist at armstrong.cc) # 2014 Nico Schottelius (nico-cdist at schottelius.org) +# 2019 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -21,4 +22,16 @@ owner="$__object_id" -getent passwd "$owner" || true +if command -v getent >/dev/null +then + getent passwd "$owner" || true +else + case $owner in + [0-9][0-9]*) + awk -F: "\$3 == \"$owner\" { print }" /etc/passwd + ;; + *) + grep "^$owner:" /etc/passwd || true + ;; + esac +fi diff --git a/cdist/conf/type/__sshd_config/explorer/state b/cdist/conf/type/__sshd_config/explorer/state new file mode 100644 index 00000000..75c68b8a --- /dev/null +++ b/cdist/conf/type/__sshd_config/explorer/state @@ -0,0 +1,121 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# Determines the current state of the config option. +# Possible output: +# - present: "should" option present in config file +# - default: the "should" option is the default -> don’t know if present +# - absent: no such option present in config file +# + +joinlines() { sed -n -e H -e "\${x;s/^\\n//;s/\\n/${1:?}/g;p;}"; } +trlower() { tr '[:upper:]' '[:lower:]'; } +tolower() { printf '%s' "$*" | trlower; } + +default_value() { + sshd -T -f /dev/null -C "$(make_conn_spec)" \ + | sed -n -e 's/^'"$(tolower "${1:?}")"'[[:blank:]]\{1,\}//p' +} + +make_conn_spec() { + if test -s "${__object:?}/parameter/match" + then + _match_file="${__object:?}/parameter/match" + else + _match_file='/dev/null' + fi + + for _kw in \ + addr=Address \ + user=User \ + host=Host \ + laddr=LocalAddress \ + lport=LocalPort \ + rdomain=RDomain + do + _specname=${_kw%%=*} + _confname=$(tolower "${_kw#*=}") + while read -r _k _v + do + if test "$(tolower "${_k}")" = "${_confname}" + then + printf '%s=%s\n' "${_specname}" "${_v}" + continue 2 + fi + done <"${_match_file}" + + # NOTE: Print test spec even for empty keys to suppress errors like: + # 'Match User' in configuration but 'user' not in connection test specification. + # except lport: + # Invalid port '' in test mode specification lport= + test "${_specname}" = 'lport' || printf '%s=\n' "${_specname}" + done \ + | joinlines ',' + unset _match_file +} + +sshd_config_file=$(cat "${__object:?}/parameter/file") +state_should=$(cat "${__object:?}/parameter/state") + +if test -s "${__object:?}/parameter/option" +then + option_name=$(cat "${__object:?}/parameter/option") +else + option_name=${__object_id:?} +fi + +value_should=$(cat "${__object:?}/parameter/value" 2>/dev/null) \ +|| test "${state_should}" = absent || exit 0 # param optional if --state absent + +command -v sshd >/dev/null 2>&1 || { + echo 'Cannot find sshd.' >&2 + exit 1 +} + +test -e "${sshd_config_file}" || { + echo 'absent' + exit 0 +} + +value_is=$( + sshd -T -f "${sshd_config_file}" -C "$(make_conn_spec)" \ + | sed -n -e 's/^'"$(tolower "${option_name}")"'[[:blank:]]\{1,\}//p') + +if printf '%s\n' "${value_is}" | { + if test -n "${value_should}" + then + grep -q -x -F "${value_should}" + else + # if no value provided, assume "any" value + grep -q -e . + fi + } +then + if default_value "${option_name}" | grep -q -x -F "${value_is}" + then + # Might produce false positives for default values. + # TODO: Manual checking should be done, but for simplicity, this case is + # currently ignored here. + echo default + else + echo present + fi +else + echo absent +fi diff --git a/cdist/conf/type/__sshd_config/files/update_sshd_config.awk b/cdist/conf/type/__sshd_config/files/update_sshd_config.awk new file mode 100644 index 00000000..f7f30e87 --- /dev/null +++ b/cdist/conf/type/__sshd_config/files/update_sshd_config.awk @@ -0,0 +1,293 @@ +# -*- mode: awk; indent-tabs-mode: t -*- + +function usage() { + print_err("Usage: awk -f update_sshd_config.awk -- -o set|unset [-m 'User git'] -l 'X11Forwarding no' /etc/ssh/sshd_config") +} + +function print_err(s) { print s | "cat >&2" } + +function alength(a, i) { + for (i = 0; (i + 1) in a; ++i); + return i +} + +function join(sep, a, i, s) { + for (i = i ? i : 1; i in a; i++) + s = s sep a[i] + return substr(s, 2) +} + +function getopt(opts, argv, target, files, i, c, lv, idx, nf) { + # trivial getopt(3) implementation; only basic functionality + if (argv[1] == "--") i++ + for (i += 1; i in argv; i++) { + if (lv) { target[c] = argv[i]; lv = 0; continue } + if (argv[i] ~ /^-/) { + c = substr(argv[i], 2, 1) + idx = index(opts, c) + if (!idx) { + print_err(sprintf("invalid option -%c\n", c)) + continue + } + if (substr(opts, idx + 1, 1) == ":") { + # option takes argument + if (length(argv[i]) > 2) + target[c] = substr(argv[i], 3) + else + lv = 1 + } else { + target[c] = 1 + } + } else + files[++nf] = argv[i] + } +} + +# tokenise configuration line +# this function mimics the counterpart in OpenSSH (misc.c) +# but it returns two (next token SUBSEP rest) because I didn’t want to have to +# simulate any pointer magic. +function strdelim_internal(s, split_equals, old) { + if (!s) + return "" + + old = s + + if (!match(s, WHITESPACE "|" QUOTE "" (split_equals ? "|" EQUALS : ""))) + return s + + s = substr(s, RSTART) + old = substr(old, 1, RSTART - 1) + + if (s ~ "^" QUOTE) { + old = substr(old, 2) + + # Find matching quote + if (match(s, QUOTE)) { + old = substr(old, 1, RSTART) + # s = substr() + if (match(s, "^" WHITESPACE "*")) + s = substr(s, RLENGTH) + return old + } else { + # no matching quote + return "" + } + } + + if (match(s, "^" WHITESPACE "+")) { + sub("^" WHITESPACE "+", "", s) + if (split_equals) + sub(EQUALS WHITESPACE "*", "", s) + } else if (s ~ "^" EQUALS) { + s = substr(s, 2) + } + + return old SUBSEP s +} +function strdelim(s) { return strdelim_internal(s, 1) } +function strdelimw(s) { return strdelim_internal(s, 0) } + +function singleton_option(opt) { + return tolower(opt) !~ /^(acceptenv|allowgroups|allowusers|denygroups|denyusers|hostcertificate|hostkey|listenaddress|logverbose|permitlisten|permitopen|port|setenv|subsystem)$/ +} + +function print_update() { + if (mode) { + if (match_only) printf "\t" + printf "%s\n", line_should + updated = 1 + } +} + +BEGIN { + FS = "\n" # disable field splitting + + WHITESPACE = "[ \t]" # servconf.c, misc.c:strdelim_internal (without line breaks, cf. bugs) + QUOTE = "[\"]" # misc.c:strdelim_internal + EQUALS = "[=]" + + split("", opts) + split("", files) + getopt("ho:l:m:", ARGV, opts, files) + + if (opts["h"]) { usage(); exit (e="0") } + + line_should = opts["l"] + match_only = opts["m"] + num_files = alength(files) + + if (num_files != 1 || !opts["o"] || !line_should) { + usage() + exit (e=126) + } + + if (opts["o"] == "set") { + mode = 1 + } else if (opts["o"] == "unset") { + mode = 0 + } else { + print_err(sprintf("invalid mode %s\n", mode)) + exit (e=1) + } + + if (mode) { + # loop over sshd_config twice! + ARGV[2] = ARGV[1] = files[1] + ARGC = 3 + } else { + # only loop once + ARGV[1] = files[1] + ARGC = 2 + } + + split(strdelim(line_should), should, SUBSEP) + option_should = tolower(should[1]) + value_should = should[2] +} + +{ + line = $0 + + # Strip trailing whitespace. Allow \f (form feed) at EOL only + sub("(" WHITESPACE "|\f)*$", "", line) + + # Strip leading whitespace + sub("^" WHITESPACE "*", "", line) + + if (match(line, "^#" WHITESPACE "*")) { + prefix = substr(line, RSTART, RLENGTH) + line = substr(line, RSTART + RLENGTH) + } else { + prefix = "" + } + + line_type = "invalid" + option_is = value_is = "" + + if (line) { + split(strdelim(line), toks, SUBSEP) + + if (tolower(toks[1]) == "match") { + MATCH = (prefix ~ /^#/ ? "#" : "") join(" ", toks, 2) + line_type = "match" + } else if (toks[1] ~ /^[A-Za-z][A-Za-z0-9]+$/) { + # This could be an option line + line_type = "option" + option_is = tolower(toks[1]) + value_is = toks[2] + } + } else { + line_type = "empty" + } +} + +# mode: unset + +!mode { + # delete matching config + if (prefix !~ /^#/) + if (MATCH == match_only && option_is == option_should) + if (!value_should || value_should == value_is) + next + + print + next +} + + +# mode: set + +mode && NR == FNR { + if (line_type == "option") { + if (MATCH !~ /^#/) { + if (prefix ~ /^#/) { + # comment line + last_occ[MATCH, "#" option_is] = FNR + } else { + # option line + last_occ[MATCH, option_is] = FNR + } + last_occ[MATCH] = FNR + } + } else if (line_type == "invalid" && !prefix) { + # INVALID LINE + print_err(sprintf("%s: syntax error on line %u\n", ARGV[0], FNR)) + } + + next +} + +# before second pass prepare hashes containing location information to be used +# in the second pass. +mode && NR > FNR && FNR == 1 { + # First we drop the locations of commented-out options if a non-commented + # option is available. If a non-commented option is available, we will + # append new config options there to have them all at one place. + for (k in last_occ) { + if (k ~ /^#/) { + # delete entries of commented out match blocks + delete last_occ[k] + continue + } + + split(k, parts, SUBSEP) + + if (parts[2] ~ /^#/ && ((parts[1], substr(parts[2], 2)) in last_occ)) + delete last_occ[k] + } + + # Reverse the option => line mapping. The line_map allows for easier lookups + # in the second pass. + # We only keep options, not top-level keywords, because we can only have + # one entry per line and there are conflicts with last lines of "sections". + for (k in last_occ) { + if (!index(k, SUBSEP)) continue + line_map[last_occ[k]] = k + } +} + +# Second pass +mode && line_map[FNR] == match_only SUBSEP option_should && !updated { + split(line_map[FNR], parts, SUBSEP) + + # If option allows multiple values, print current value + if (!singleton_option(parts[2])) { + if (value_should != value_is) + print + } + + print_update() + + next +} + +mode { print } + +# Is a comment option +mode && line_map[FNR] == match_only SUBSEP "#" option_should && !updated { + print_update() +} + +# Last line of the should match section +mode && last_occ[match_only] == FNR && !updated { + # NOTE: Inserting empty lines is only cosmetic. It is only done if + # different options are next to each other and not in a match block + # (match blocks are usually not in the default config and thus don’t + # contain commented blocks.) + if (line && option_is != option_should && !MATCH) + print "" + print_update() +} + +END { + if (e) exit e + + if (mode && !updated) { + if (match_only && MATCH != match_only) { + printf "\nMatch %s\n", match_only + } + + print_update() + } +} diff --git a/cdist/conf/type/__sshd_config/gencode-remote b/cdist/conf/type/__sshd_config/gencode-remote new file mode 100755 index 00000000..275db4aa --- /dev/null +++ b/cdist/conf/type/__sshd_config/gencode-remote @@ -0,0 +1,98 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +joinlines() { sed -n -e H -e "\${x;s/^\\n//;s/\\n/${1:?}/g;p;}"; } + +state_is=$(cat "${__object:?}/explorer/state") +state_should=$(cat "${__object:?}/parameter/state") + +if test "${state_is}" = "${state_should}" -o "${state_is}" = 'default' +then + # nothing to do (if the value is the default, ignore its state) + exit 0 +fi + +case ${state_should} +in + (present) + mode='set' + ;; + (absent) + mode='unset' + ;; + (*) + printf 'Invalid --state: %s\n' "${state_should}" >&2 + exit 1 + ;; +esac + +sshd_config_file=$(cat "${__object:?}/parameter/file") + +quote() { printf "'%s'" "$(printf '%s' "$*" | sed -e "s/'/'\\\\''/g")"; } +drop_awk_comments() { quote "$(sed '/^[[:blank:]]*#.*$/d;/^$/d' "$@")"; } + +# Ensure the sshd_config file is there +cat <$(quote "${sshd_config_file}") + chown 0:0 $(quote "${sshd_config_file}") + chmod 0644 $(quote "${sshd_config_file}") +} + +EOF + +match_only= +if test -s "${__object:?}/parameter/match" +then + match_only=$(joinlines ' ' <"${__object:?}/parameter/match") +fi + +if test -s "${__object:?}/parameter/option" +then + option_line=$(cat "${__object:?}/parameter/option") +else + option_line=${__object_id:?} +fi + +if test -s "${__object:?}/parameter/value" +then + option_line="${option_line} $(cat "${__object:?}/parameter/value")" +fi + +# Send message on config update +printf '%s%s %s\n' "${mode}" "${match_only:+ [${match_only}]}" \ + "${option_line}" >>"${__messages_out:?}" + +# Update sshd_config (remote code) +cat <$(quote "${sshd_config_file}.tmp") \\ +|| exit + +cmp -s $(quote "${sshd_config_file}") $(quote "${sshd_config_file}.tmp") || { + sshd -t -f $(quote "${sshd_config_file}.tmp") \\ + && cat $(quote "${sshd_config_file}.tmp") >$(quote "${sshd_config_file}") \\ + || exit # stop if sshd_config file check fails +} +rm -f $(quote "${sshd_config_file}.tmp") +EOF diff --git a/cdist/conf/type/__sshd_config/man.rst b/cdist/conf/type/__sshd_config/man.rst new file mode 100644 index 00000000..c8e6b8ad --- /dev/null +++ b/cdist/conf/type/__sshd_config/man.rst @@ -0,0 +1,98 @@ +cdist-type__sshd_config(7) +========================== + +NAME +---- +cdist-type__sshd_config - Manage options in sshd_config + + +DESCRIPTION +----------- +This space intentionally left blank. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +file + The path to the sshd_config file to edit. + Defaults to ``/etc/ssh/sshd_config``. +match + Restrict this option to apply only for certain connections. + Allowed values are what would be allowed to be written after a ``Match`` + keyword in ``sshd_config``, e.g. ``--match 'User anoncvs'``. + + Can be used multiple times. All of the values are ANDed together. +option + The name of the option to manipulate. Defaults to ``__object_id``. +state + Can be: + + - ``present``: ensure a matching config line is present (or the default + value). + - ``absent``: ensure no matching config line is present. +value + The option's value to be assigned to the option (if ``--state present``) or + removed (if ``--state absent``). + + This option is required if ``--state present``. If not specified and + ``--state absent``, all values for the given option are removed. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Disallow root logins with password + __sshd_config PermitRootLogin --value without-password + + # Disallow password-based authentication + __sshd_config PasswordAuthentication --value no + + # Accept the EDITOR environment variable + __sshd_config AcceptEnv:EDITOR --option AcceptEnv --value EDITOR + + # Force command for connections as git user + __sshd_config git@ForceCommand --match 'User git' --option ForceCommand \ + --value 'cd ~git && exec git-shell ${SSH_ORIGINAL_COMMAND:+-c "${SSH_ORIGINAL_COMMAND}"}' + + +SEE ALSO +-------- +:strong:`sshd_config`\ (5) + + +BUGS +---- +- This type assumes a nicely formatted config file, + i.e. no config options spanning multiple lines. +- ``Include`` directives are ignored. +- Config options are not added/removed to/from the config file if their value is + the default value. +- | The explorer will incorrectly report ``absent`` if OpenSSH internally + transforms one value to another (e.g. ``permitrootlogin prohibit-password`` + is transformed to ``permitrootlogin without-password``). + | Workaround: Use the value that OpenSSH uses internally. + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__sshd_config/manifest b/cdist/conf/type/__sshd_config/manifest new file mode 100755 index 00000000..566bde90 --- /dev/null +++ b/cdist/conf/type/__sshd_config/manifest @@ -0,0 +1,48 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +os=$(cat "${__global:?}/explorer/os") + +state_should=$(cat "${__object:?}/parameter/state") + +case ${os} +in + (alpine|centos|fedora|redhat|scientific|debian|devuan|ubuntu) + if test "${state_should}" != 'absent' + then + __package openssh-server --state present + fi + ;; + (archlinux|gentoo|slackware|suse) + if test "${state_should}" != 'absent' + then + __package openssh --state present + fi + ;; + (freebsd|netbsd|openbsd) + # whitelist + ;; + (*) + printf 'Your operating system (%s) is currently not supported by this type (%s)\n' \ + "${os}" "${__type##*/}" >&2 + printf 'Please contribute an implementation for it if you can.\n' >&2 + exit 1 + ;; +esac diff --git a/cdist/conf/type/__sshd_config/parameter/default/file b/cdist/conf/type/__sshd_config/parameter/default/file new file mode 100644 index 00000000..d8ea5dfc --- /dev/null +++ b/cdist/conf/type/__sshd_config/parameter/default/file @@ -0,0 +1 @@ +/etc/ssh/sshd_config diff --git a/cdist/conf/type/__sshd_config/parameter/default/state b/cdist/conf/type/__sshd_config/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__sshd_config/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__sshd_config/parameter/optional b/cdist/conf/type/__sshd_config/parameter/optional new file mode 100644 index 00000000..922ab093 --- /dev/null +++ b/cdist/conf/type/__sshd_config/parameter/optional @@ -0,0 +1,4 @@ +file +option +state +value diff --git a/cdist/conf/type/__sshd_config/parameter/optional_multiple b/cdist/conf/type/__sshd_config/parameter/optional_multiple new file mode 100644 index 00000000..02b1d1a9 --- /dev/null +++ b/cdist/conf/type/__sshd_config/parameter/optional_multiple @@ -0,0 +1 @@ +match diff --git a/cdist/conf/type/__staged_file/gencode-local b/cdist/conf/type/__staged_file/gencode-local index 8e2003af..ba9e8798 100755 --- a/cdist/conf/type/__staged_file/gencode-local +++ b/cdist/conf/type/__staged_file/gencode-local @@ -23,7 +23,6 @@ destination="$__object_id" source="$(cat "$__object/parameter/source")" -cksum="$(cat "$__object/parameter/cksum")" stage_dir="$(cat "$__object/parameter/stage-dir")" state="$(cat "$__object/parameter/state")" fetch_command="$(cat "$__object/parameter/fetch-command")" @@ -57,24 +56,30 @@ get_file() { } fetch_file() { + # shellcheck disable=SC2059 printf "$fetch_command" "$source" printf ' > "%s"\n' "$stage_file" } fetch_and_prepare_file() { - printf 'tmpdir="$(mktemp -d --tmpdir="/tmp" "%s")"\n' "${__type##*/}.XXXXXXXXXX" + # shellcheck disable=SC2016 + printf 'tmpdir="$(mktemp -d -p "/tmp" "%s")"\n' "${__type##*/}.XXXXXXXXXX" + # shellcheck disable=SC2016 printf 'cd "$tmpdir"\n' - printf "$fetch_command > \"%s\"\n" "$source" "$source_file_name" + # shellcheck disable=SC2059 + printf "$fetch_command > \"%s\"\\n" "$source" "$source_file_name" prepare_command="$(cat "$__object/parameter/prepare-command")" - printf "$prepare_command > \"%s\"\n" "$source_file_name" "$stage_file" + # shellcheck disable=SC2059 + printf "$prepare_command > \"%s\"\\n" "$source_file_name" "$stage_file" printf 'cd - >/dev/null\n' + # shellcheck disable=SC2016 printf 'rm -rf "$tmpdir"\n' } cat << DONE verify_cksum() { cksum_is="\$(cksum "$stage_file" | cut -d' ' -f1,2)" - cksum_should="$(cat "$__object/parameter/cksum" | cut -d' ' -f1,2)" + cksum_should="$(cut -d' ' -f1,2 "$__object/parameter/cksum")" if [ "\$cksum_is" = "\$cksum_should" ]; then return 0 else diff --git a/cdist/conf/type/__staged_file/manifest b/cdist/conf/type/__staged_file/manifest index 1654e1d9..c8e1fbbb 100755 --- a/cdist/conf/type/__staged_file/manifest +++ b/cdist/conf/type/__staged_file/manifest @@ -19,11 +19,7 @@ # destination="$__object_id" -source="$(cat "$__object/parameter/source")" -cksum="$(cat "$__object/parameter/cksum")" stage_dir="$(cat "$__object/parameter/stage-dir")" -state="$(cat "$__object/parameter/state")" -fetch_command="$(cat "$__object/parameter/fetch-command")" stage_file="${stage_dir}/${destination}" set -- "/${destination}" diff --git a/cdist/conf/type/__start_on_boot/explorer/state b/cdist/conf/type/__start_on_boot/explorer/state index d8f9b7ba..b7a6cf0f 100644 --- a/cdist/conf/type/__start_on_boot/explorer/state +++ b/cdist/conf/type/__start_on_boot/explorer/state @@ -1,6 +1,6 @@ #!/bin/sh # -# 2012-2015 Nico Schottelius (nico-cdist at schottelius.org) +# 2012-2019 Nico Schottelius (nico-cdist at schottelius.org) # 2013 Daniel Heule (hda at sfs.biz) # # This file is part of cdist. @@ -38,12 +38,27 @@ if [ "$init" = 'systemd' ]; then else case "$os" in debian|openwrt|devuan) - state="present" - [ -f "/etc/rc$runlevel.d/S"??"$name" ] || state="absent" + state="absent" + for file in "/etc/rc$runlevel.d/S"??"$name" + do + if [ -f "$file" ] + then + state="present" + break + fi + done ;; ubuntu) state="absent" - [ -f "/etc/rc$runlevel.d/S"??"$name" ] && state="present" + for file in "/etc/rc$runlevel.d/S"??"$name" + do + if [ -f "$file" ] + then + state="present" + break + fi + done + [ -f "/etc/init/${name}.conf" ] && state="present" ;; @@ -60,14 +75,25 @@ else state=$(chkconfig --check "$name" "$runlevel" || echo absent) [ "$state" ] || state="present" ;; - gentoo) - state="present" - [ -f "/etc/runlevels/${target_runlevel}/${name}" ] || state="absent" + gentoo|alpine) + state="absent" + for d in /etc/runlevels/*; do + if [ -f "/etc/runlevels/${d}/${name}" ];then + state="present" + break + fi + done ;; freebsd) state="absent" service -e | grep "/$name$" && state="present" ;; + openbsd) + state='absent' + # OpenBSD 5.7 and higher + rcctl ls on | grep "^${name}$" && state='present' + ;; + *) echo "Unsupported os: $os" >&2 exit 1 diff --git a/cdist/conf/type/__start_on_boot/gencode-remote b/cdist/conf/type/__start_on_boot/gencode-remote index 14ee7dab..c900933f 100755 --- a/cdist/conf/type/__start_on_boot/gencode-remote +++ b/cdist/conf/type/__start_on_boot/gencode-remote @@ -37,16 +37,16 @@ case "$state_should" in if [ "$init" = 'systemd' ]; then # this handles ALL linux distros with systemd # e.g. archlinux, gentoo in some cases, new RHEL and SLES versions - echo "systemctl -q enable \"$name\"" + echo "systemctl -q enable '$name'" else case "$os" in debian) case "$os_version" in [1-7]*) - echo "update-rc.d \"$name\" defaults >/dev/null" + echo "update-rc.d '$name' defaults >/dev/null" ;; 8*) - echo "systemctl enable \"$name\"" + echo "systemctl enable '$name'" ;; *) echo "Unsupported version $os_version of $os" >&2 @@ -55,32 +55,37 @@ case "$state_should" in esac ;; devuan) - echo "update-rc.d \"$name\" defaults >/dev/null" + echo "update-rc.d '$name' defaults >/dev/null" ;; - gentoo) - echo rc-update add \"$name\" \"$target_runlevel\" + alpine|gentoo) + echo "rc-update add '$name' '$target_runlevel'" ;; amazon|scientific|centos|fedora|owl|redhat|suse) - echo chkconfig \"$name\" on + echo "chkconfig '$name' on" ;; openwrt) # 'enable' can be successful and still return a non-zero exit # code, deal with it by checking for success ourselves in that # case (the || ... part). - echo "/etc/init.d/\"$name\" enable || [ -f /etc/rc.d/S??\"$name\" ]" + echo "'/etc/init.d/$name' enable || [ -f /etc/rc.d/S??'$name' ]" ;; ubuntu) - echo "update-rc.d \"$name\" defaults >/dev/null" + echo "update-rc.d '$name' defaults >/dev/null" ;; freebsd) : # handled in manifest ;; + openbsd) + # OpenBSD 5.7 and higher + echo "rcctl enable '$name'" + ;; + *) echo "Unsupported os: $os" >&2 exit 1 @@ -93,24 +98,29 @@ case "$state_should" in if [ "$init" = 'systemd' ]; then # this handles ALL linux distros with systemd # e.g. archlinux, gentoo in some cases, new RHEL and SLES versions - echo "systemctl -q disable \"$name\"" + echo "systemctl -q disable '$name'" else case "$os" in debian|ubuntu|devuan) - echo update-rc.d -f \"$name\" remove + echo "update-rc.d -f '$name' remove" ;; - gentoo) - echo rc-update del \"$name\" \"$target_runlevel\" + alpine|gentoo) + echo "rc-update del '$name' '$target_runlevel'" ;; centos|fedora|owl|redhat|suse) - echo chkconfig \"$name\" off + echo "chkconfig '$name' off" ;; openwrt) - echo "\"/etc/init.d/$name\" disable" + echo "'/etc/init.d/$name' disable" + ;; + + openbsd) + # OpenBSD 5.7 and higher + echo "rcctl disable '$name'" ;; *) diff --git a/cdist/conf/type/__start_on_boot/man.rst b/cdist/conf/type/__start_on_boot/man.rst index 851d1a89..f8afe94b 100644 --- a/cdist/conf/type/__start_on_boot/man.rst +++ b/cdist/conf/type/__start_on_boot/man.rst @@ -12,7 +12,7 @@ This cdist type allows you to enable or disable stuff to be started at boot of your operating system. Warning: This type has not been tested intensively and is not fully -supported (i.e. \*BSD are not implemented). +supported. REQUIRED PARAMETERS @@ -55,7 +55,7 @@ Nico Schottelius COPYING ------- -Copyright \(C) 2012 Nico Schottelius. You can redistribute it +Copyright \(C) 2012-2019 Nico Schottelius. You can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/cdist/conf/type/__start_on_boot/manifest b/cdist/conf/type/__start_on_boot/manifest index b9ee20e2..c1c983ec 100644 --- a/cdist/conf/type/__start_on_boot/manifest +++ b/cdist/conf/type/__start_on_boot/manifest @@ -16,7 +16,7 @@ case "$os" in else value='NO' fi - __key_value rcconf-$name-enable \ + __key_value "rcconf-$name-enable" \ --file /etc/rc.conf \ --key "${name}_enable" \ --value "\"$value\"" \ diff --git a/cdist/conf/type/__hostname/explorer/hostname_sysconfig b/cdist/conf/type/__sysctl/explorer/conf-path similarity index 76% rename from cdist/conf/type/__hostname/explorer/hostname_sysconfig rename to cdist/conf/type/__sysctl/explorer/conf-path index d0d7b4e7..ba35c4c6 100755 --- a/cdist/conf/type/__hostname/explorer/hostname_sysconfig +++ b/cdist/conf/type/__sysctl/explorer/conf-path @@ -1,6 +1,6 @@ #!/bin/sh # -# 2014 Nico Schottelius (nico-cdist at schottelius.org) +# 2018 Darko Poljak (darko.poljak at gmail.com) # # This file is part of cdist. # @@ -17,10 +17,9 @@ # You should have received a copy of the GNU General Public License # along with cdist. If not, see . # -# -# Retrieve the contents of /etc/hostname -# -if [ -f /etc/sysconfig/network ]; then - awk -F= '/^HOSTNAME=/ { print $2 }' /etc/sysconfig/network +if [ -d "/etc/sysctl.d" ]; then + echo "/etc/sysctl.d/99-Z-sysctl-cdist.conf"; +else + echo "/etc/sysctl.conf"; fi diff --git a/cdist/conf/type/__sysctl/explorer/value b/cdist/conf/type/__sysctl/explorer/value index fc85b3d8..3e93c151 100755 --- a/cdist/conf/type/__sysctl/explorer/value +++ b/cdist/conf/type/__sysctl/explorer/value @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh -e # # 2014 Steven Armstrong (steven-cdist at armstrong.cc) # @@ -18,5 +18,10 @@ # along with cdist. If not, see . # +if test "$(uname -s)" = NetBSD +then + PATH=$(getconf PATH) +fi + # get the current runtime value -sysctl -n "$__object_id" || true +sysctl -n "${__object_id}" || true diff --git a/cdist/conf/type/__sysctl/gencode-remote b/cdist/conf/type/__sysctl/gencode-remote index a7aedb84..f0f6deef 100755 --- a/cdist/conf/type/__sysctl/gencode-remote +++ b/cdist/conf/type/__sysctl/gencode-remote @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2018 Takashi Yoshi (takashi at yoshi.email) # # This file is part of cdist. # @@ -28,12 +29,28 @@ fi os=$(cat "$__global/explorer/os") case "$os" in - redhat|centos|ubuntu|debian|devuan|archlinux|coreos) - flag='-w' - ;; - frebsd) - flag='' - ;; + # Linux + redhat|centos|ubuntu|debian|devuan|archlinux|gentoo|coreos) + flag='-w' + ;; + # BusyBox + alpine|openwrt) + flag='-w' + ;; + macosx) + # NOTE: Older versions of Mac OS X require the -w option. + # Even though the flag is not mentioned in new man pages anymore, + # it still works. + flag='-w' + ;; + netbsd) + # shellcheck disable=SC2016 + echo 'PATH=$(getconf PATH)' + flag='-w' + ;; + freebsd|openbsd) + flag='' + ;; esac # set the current runtime value diff --git a/cdist/conf/type/__sysctl/man.rst b/cdist/conf/type/__sysctl/man.rst index 6873003e..dbb9a1ac 100644 --- a/cdist/conf/type/__sysctl/man.rst +++ b/cdist/conf/type/__sysctl/man.rst @@ -26,6 +26,13 @@ EXAMPLES __sysctl net.ipv4.ip_forward --value 1 + # On some operating systems, e.g. NetBSD, to prevent an error if the + # MIB style name does not exist (e.g. optional kernel components), + # name and value can be separated by `?=`. The same effect can be achieved + # in cdist by appending a `?` to the key: + + __sysctl ddb.onpanic? --value -1 + AUTHORS ------- diff --git a/cdist/conf/type/__sysctl/manifest b/cdist/conf/type/__sysctl/manifest index c903dbae..71dea7f7 100755 --- a/cdist/conf/type/__sysctl/manifest +++ b/cdist/conf/type/__sysctl/manifest @@ -1,6 +1,8 @@ #!/bin/sh -e # # 2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2018 Takashi Yoshi (takashi at yoshi.email) +# 2019 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # @@ -22,7 +24,12 @@ os=$(cat "$__global/explorer/os") case "$os" in - redhat|centos|ubuntu|debian|devuan|archlinux|coreos|freebsd) + # Linux + alpine|redhat|centos|ubuntu|debian|devuan|archlinux|coreos) + : + ;; + # BSD + freebsd|macosx|netbsd|openbsd) : ;; *) @@ -32,8 +39,10 @@ case "$os" in ;; esac +conf_path=$(cat "$__object/explorer/conf-path") + __key_value "$__object_name" \ --key "$__object_id" \ - --file /etc/sysctl.conf \ + --file "${conf_path}" \ --value "$(cat "$__object/parameter/value")" \ --delimiter '=' diff --git a/cdist/conf/type/__systemd_service/explorer/state b/cdist/conf/type/__systemd_service/explorer/state new file mode 100755 index 00000000..f5f751d4 --- /dev/null +++ b/cdist/conf/type/__systemd_service/explorer/state @@ -0,0 +1,43 @@ +#!/bin/sh -e +# explorer/state +# +# 2020 Matthias Stecher +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +# Check if the service is running or stopped. +# +# The explorer must check before if the service exist, because 'systemctl is-active' +# will return "inactive" even if there is no service there: +# systemctl cat foo # does not exist +# systemctl is-active foo # is "inactive" + + +# get name of the service +if [ -f "$__object/parameter/name" ]; then + name="$(cat "$__object/parameter/name")" +else + name="$__object_id" +fi + + +# check if the service exist, else exit without output (also if systemd doesn't exist) +# do not exit here with an error code, will be done in the gencode-remote script +systemctl cat "$name" > /dev/null 2>&1 || exit 0 + +# print if the service is running or not +systemctl is-active -q "$name" && printf "running" || printf "stopped" diff --git a/cdist/conf/type/__systemd_service/gencode-remote b/cdist/conf/type/__systemd_service/gencode-remote new file mode 100755 index 00000000..c867ff22 --- /dev/null +++ b/cdist/conf/type/__systemd_service/gencode-remote @@ -0,0 +1,98 @@ +#!/bin/sh -e +# gencode-remote +# +# 2020 Matthias Stecher +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +# Checks the given state of the service and set it to the given +# state. Optionally, it executes the action if service running. + + +# get name of the service +name="$__object/parameter/name" +if [ -f "$name" ]; then + name="$(cat "$name")" +else + name="$__object_id" +fi + + +# read current status and parameters +state="$(cat "$__object/explorer/state")" +should="$(cat "$__object/parameter/state")" + +# if systemd/service does not exist +if [ -z "$state" ]; then + printf "systemd or service '%s' does not exist!\n" "$name" >&2 + exit 1 +fi + + +# save the action required +required_action="" + +# check the state of the service that should be +if [ "$state" != "$should" ]; then + # select what to do to get the $should state + case "$should" in + running) + if [ "$state" = "stopped" ]; then required_action="start"; fi + ;; + + stopped) + if [ "$state" = "running" ]; then required_action="stop"; fi + ;; + esac +fi + +# check if the action can be achieved if given +if [ -f "$__object/parameter/action" ] \ + && [ -z "$required_action" ] && [ "$state" = "running" ]; then + + # there must be an action + action="$(cat "$__object/parameter/action")" + + # select the action to the required element + case "$action" in + restart) + required_action="restart" + ;; + + reload) + required_action="reload" + ;; + + *) + printf "action '%s' does not exist!" "$action" >&2 + exit 2 + esac + + # Make a special check: only do this action if a dependency did something + # it is required that the dependencies write there action to $__messages_in + if [ -f "$__object/parameter/if-required" ]; then + # exit here if there are no changes from the dependencies affected (nothing to do) + if ! grep -q -f "$__object/require" "$__messages_in"; then exit 0; fi + fi +fi + +# print the execution command if a action given +if [ -n "$required_action" ]; then + # also print it as message + echo "$required_action" >> "$__messages_out" + echo "systemctl $required_action '$name'" +fi diff --git a/cdist/conf/type/__systemd_service/man.rst b/cdist/conf/type/__systemd_service/man.rst new file mode 100644 index 00000000..cd14c985 --- /dev/null +++ b/cdist/conf/type/__systemd_service/man.rst @@ -0,0 +1,117 @@ +cdist-type__systemd_service(7) +============================== + +NAME +---- +cdist-type__systemd_service - Controls a systemd service state + + +DESCRIPTION +----------- +This type controls systemd services to define a state of the service, +or an action like reloading or restarting. It is useful to reload a +service after configuration applied or shutdown one service. + +The activation or deactivation is out of scope. Look for the +:strong:`cdist-type__systemd_util`\ (7) type instead. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- + +name + String which will used as name instead of the object id. + +state + The state which the service should be in: + + running + Service should run (default) + + stopped + Service should be stopped + +action + Executes an action on on the service. It will only execute it if the + service keeps the state ``running``. There are following actions, where: + + reload + Reloads the service + + restart + Restarts the service + +BOOLEAN PARAMETERS +------------------ + +if-required + Only execute the action if at minimum one required type outputs a message + to ``$__messages_out``. Through this, the action should only executed if a + dependency did something. The action will not executed if no dependencies + given. + + +MESSAGES +-------- + +start + Started the service + +stop + Stopped the service + +restart + Restarted the service + +reload + Reloaded the service + + +ABORTS +------ +Aborts in following cases: + +systemd or the service does not exist + + +EXAMPLES +-------- +.. code-block:: sh + + # service must run + __systemd_service nginx + + # service must stopped + __systemd_service sshd \ + --state stopped + + # restart the service + __systemd_service apache2 \ + --action restart + + # makes sure the service exist with an alternative name + __systemd_service foo \ + --name sshd + + # reload the service for a modified configuration file + # only reloads the service if the file really changed + require="__file/etc/foo.conf" __systemd_service foo \ + --action reload --if-required + + +AUTHORS +------- +Matthias Stecher + + +COPYRIGHT +--------- +Copyright \(C) 2020 Matthias Stecher. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__systemd_service/parameter/boolean b/cdist/conf/type/__systemd_service/parameter/boolean new file mode 100644 index 00000000..a4bccb66 --- /dev/null +++ b/cdist/conf/type/__systemd_service/parameter/boolean @@ -0,0 +1 @@ +if-required diff --git a/cdist/conf/type/__systemd_service/parameter/default/state b/cdist/conf/type/__systemd_service/parameter/default/state new file mode 100644 index 00000000..a2ae71b3 --- /dev/null +++ b/cdist/conf/type/__systemd_service/parameter/default/state @@ -0,0 +1 @@ +running diff --git a/cdist/conf/type/__systemd_service/parameter/optional b/cdist/conf/type/__systemd_service/parameter/optional new file mode 100644 index 00000000..fc78265f --- /dev/null +++ b/cdist/conf/type/__systemd_service/parameter/optional @@ -0,0 +1,3 @@ +name +state +action diff --git a/cdist/conf/type/__systemd_unit/gencode-remote b/cdist/conf/type/__systemd_unit/gencode-remote index c608d9b3..967a6c87 100644 --- a/cdist/conf/type/__systemd_unit/gencode-remote +++ b/cdist/conf/type/__systemd_unit/gencode-remote @@ -23,24 +23,34 @@ state=$(cat "${__object}/parameter/state") current_enablement_state=$(cat "${__object}/explorer/enablement-state") if [ "${state}" = "absent" ]; then - if [ ! -z "${current_enablement_state}" ]; then + if [ -n "${current_enablement_state}" ]; then echo "systemctl --now disable ${name}" + echo "rm -f /etc/systemd/system/${name}" + echo "systemctl daemon-reload" fi exit 0 fi unit_status=$(cat "${__object}/explorer/unit-status") +desired_enablement_state=$(cat "${__object}/parameter/enablement-state") + +if [ "${current_enablement_state}" = "masked" ] && \ + [ "${desired_enablement_state}" != "masked" ]; then + echo "systemctl unmask ${name}" +fi if [ -f "${__object}/parameter/restart" ]; then - if grep -q "^__file/etc/systemd/system/${name}" "${__messages_in}" || \ + if [ "${desired_enablement_state}" = "masked" ]; then + if [ "${unit_status}" = "active" ]; then + echo "systemctl stop ${name}" + fi + elif grep -q "^__file/etc/systemd/system/${name}" "${__messages_in}" || \ [ "${unit_status}" != "active" ]; then echo "systemctl restart ${name} || true" fi fi -desired_enablement_state=$(cat "${__object}/parameter/enablement-state") - if [ "${current_enablement_state}" = "${desired_enablement_state}" ]; then exit 0 fi @@ -56,6 +66,9 @@ case "${desired_enablement_state}" in disabled) echo "systemctl disable ${name}" ;; + masked) + echo "systemctl mask ${name}" + ;; *) echo "Unsupported unit status: ${desired_enablement_state}" >&2 exit 1 diff --git a/cdist/conf/type/__systemd_unit/man.rst b/cdist/conf/type/__systemd_unit/man.rst index 88da6b30..25a4e501 100644 --- a/cdist/conf/type/__systemd_unit/man.rst +++ b/cdist/conf/type/__systemd_unit/man.rst @@ -9,9 +9,10 @@ cdist-type__systemd_unit - Install a systemd unit DESCRIPTION ----------- -This type can install, enable and start a systemd unit. This is particularly -useful on systems which take advantage of systemd heavily (e.g., CoreOS). For -more information about systemd units, see SYSTEMD.UNIT(5). +This type manages systemd units in ``/etc/systemd/system/``. It can install, +enable and start a systemd unit. This is particularly useful on systems which +take advantage of systemd heavily (e.g., CoreOS). For more information about +systemd units, see SYSTEMD.UNIT(5). REQUIRED PARAMETERS ------------------- @@ -22,12 +23,14 @@ OPTIONAL PARAMETERS ------------------- enablement-state - 'enabled' or 'disabled', where: + 'enabled', 'disabled' or 'masked', where: enabled enables the unit disabled disables the unit + masked + masks the unit source Path to the config file. If source is '-' (dash), take what was written to @@ -37,15 +40,17 @@ state 'present' or 'absent', defaults to 'present' where: present - the unit is installed, enabled and started + the unit (or its mask) is installed absent - the unit is stopped, disabled and uninstalled + The unit is stopped, disabled and uninstalled. If the unit was masked, + the mask is removed. BOOLEAN PARAMETERS ------------------ restart - Restart the unit on unit file change or when the unit is inactive. + Start the unit if it was inactive. Restart the unit if the unit file + changed. Stop the unit if new ``enablement-state`` is ``masked``. MESSAGES -------- diff --git a/cdist/conf/type/__systemd_unit/manifest b/cdist/conf/type/__systemd_unit/manifest index 8b136605..688a00b1 100644 --- a/cdist/conf/type/__systemd_unit/manifest +++ b/cdist/conf/type/__systemd_unit/manifest @@ -29,8 +29,11 @@ fi name="${__object_id}" source=$(cat "${__object}/parameter/source") state=$(cat "${__object}/parameter/state") +enablement_state=$(cat "${__object}/parameter/enablement-state") -if [ -z "${source}" ] && [ "${state}" != "absent" ]; then +# The unit must be disabled before removing its unit file. The unit file is +# therefore removed by gencode-remote of this type, not here. +if [ -z "${source}" ] || [ "${state}" = "absent" ]; then exit 0 fi @@ -39,8 +42,17 @@ if [ "${source}" = "-" ]; then source="${__object}/stdin" fi +unitfile_state="${state}" +if [ "${enablement_state}" = "masked" ]; then + # Masking creates a symlink from /etc/systemd/system/ to /dev/null. + # This process fails with "Failed to execute operation: Invalid argument" + # if file /etc/systemd/system/ already exists. We must therefore + # remove it. + unitfile_state="absent" +fi + __config_file "/etc/systemd/system/${name}" \ --mode 644 \ --onchange "systemctl daemon-reload" \ --source "${source}" \ - --state "${state}" + --state "${unitfile_state}" diff --git a/cdist/conf/type/__timezone/gencode-remote b/cdist/conf/type/__timezone/gencode-remote index 1f3f0196..b685c990 100755 --- a/cdist/conf/type/__timezone/gencode-remote +++ b/cdist/conf/type/__timezone/gencode-remote @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2012 Steven Armstrong (steven-cdist at armstrong.cc) +# 2019 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # @@ -21,7 +22,7 @@ # This type allows to configure the desired localtime timezone. timezone_is=$(cat "$__object/explorer/timezone_is") -timezone_should="$__object_id" +timezone_should=$(cat "$__object/parameter/tz") os=$(cat "$__global/explorer/os") if [ "$timezone_is" = "$timezone_should" ]; then @@ -29,7 +30,7 @@ if [ "$timezone_is" = "$timezone_should" ]; then fi case "$os" in - ubuntu|debian|devuan|coreos) + ubuntu|debian|devuan|coreos|alpine) echo "echo \"$timezone_should\" > /etc/timezone" ;; esac diff --git a/cdist/conf/type/__timezone/man.rst b/cdist/conf/type/__timezone/man.rst index 8a945c16..6012c552 100644 --- a/cdist/conf/type/__timezone/man.rst +++ b/cdist/conf/type/__timezone/man.rst @@ -14,7 +14,8 @@ This type creates a symlink (/etc/localtime) to the selected timezone REQUIRED PARAMETERS ------------------- -None. +tz + The name of timezone to set. OPTIONAL PARAMETERS @@ -27,19 +28,24 @@ EXAMPLES .. code-block:: sh - #Set up Europe/Andorra as our timezone. - __timezone Europe/Andorra + # Set up Europe/Andorra as our timezone. + __timezone --tz Europe/Andorra - #Set up US/Central as our timezone. - __timezone US/Central + # Set up US/Central as our timezone. + __timezone --tz US/Central AUTHORS ------- -Ramon Salvadó +| Steven Armstrong +| Nico Schottelius +| Ramon Salvadó +| Dennis Camera COPYING ------- -Free use of this software is -granted under the terms of the GNU General Public License version 3 (GPLv3). +Copyright \(C) 2012-2020 the `AUTHORS`_. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__timezone/manifest b/cdist/conf/type/__timezone/manifest index a20f5d32..0eb7fb9c 100755 --- a/cdist/conf/type/__timezone/manifest +++ b/cdist/conf/type/__timezone/manifest @@ -2,7 +2,7 @@ # # 2011 Ramon Salvadó (rsalvado at gnuine dot com) # 2012-2015 Steven Armstrong (steven-cdist at armstrong.cc) -# 2012 Nico Schottelius (nico-cdist at schottelius.org) +# 2012-2019 Nico Schottelius (nico-cdist at schottelius.org) # # This file is part of cdist. # @@ -22,11 +22,11 @@ # # This type allows to configure the desired localtime timezone. -timezone="$__object_id" +timezone=$(cat "$__object/parameter/tz") os=$(cat "$__global/explorer/os") case "$os" in - archlinux|debian|ubuntu|devuan) + archlinux|debian|ubuntu|devuan|alpine) __package tzdata export require="__package/tzdata" ;; @@ -34,7 +34,11 @@ case "$os" in __package timezone export require="__package/timezone" ;; - freebsd|netbsd|coreos) + freebsd|netbsd|openbsd) + # whitelist + : + ;; + coreos) # whitelist : ;; diff --git a/cdist/conf/type/__timezone/parameter/required b/cdist/conf/type/__timezone/parameter/required new file mode 100644 index 00000000..975445e4 --- /dev/null +++ b/cdist/conf/type/__timezone/parameter/required @@ -0,0 +1 @@ +tz diff --git a/cdist/conf/type/__timezone/singleton b/cdist/conf/type/__timezone/singleton new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__uci/explorer/state b/cdist/conf/type/__uci/explorer/state new file mode 100644 index 00000000..d7363dbf --- /dev/null +++ b/cdist/conf/type/__uci/explorer/state @@ -0,0 +1,110 @@ +#!/bin/sh +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer retrieves the current state of the configuration option +# The output of this explorer is one of these values: +# present +# The configuration option is present and has the value of the +# parameter --value. +# absent +# The configuration option is not defined. +# different +# The configuration option is present but has a different value than the +# parameter --value. +# rearranged +# The configuration option is present (a list) and has the same values as +# the parameter --value, but in a different order. + +RS=$(printf '\036') + +option=${__object_id:?} + +values_is=$(uci -s -N -d "${RS}" get "${option}" 2>/dev/null) || { + echo absent + exit 0 +} + +if test -f "${__object:?}/parameter/value" +then + should_file="${__object:?}/parameter/value" +else + should_file='/dev/null' +fi + + +# strip off trailing newline +printf '%s' "${values_is}" \ +| awk ' +function unquote(s) { + # simplified dequoting of single quoted strings + if (s ~ /^'\''.*'\''$/) { + s = substr(s, 2, length(s) - 2) + sub(/'"'\\\\''"'/, "'\''", s) + } + return s +} + +BEGIN { + state = "present" # assume all is fine +} +NR == FNR { + # memoize "should" state + should[FNR] = $0 + should_count++ + + # go to next line (important!) + next +} + +# compare "is" state + +{ $0 = unquote($0) } + +$0 == should[FNR] { next } + +FNR > should_count { + # there are more "is" records than "should" -> definitely different + state = "different" + exit +} + +{ + # see if we can find the value somewhere in should + for (i in should) { + if ($0 == should[i]) { + # ... value found -> rearranged + # FIXME: Duplicate values are not properly handled here. Do they matter? + state = "rearranged" + next + } + } + + state = "different" + exit +} + +END { + if (FNR < should_count) { + # "is" was shorter than "should" -> different + state = "different" + } + + print state +} +' "${should_file}" RS="${RS}" - diff --git a/cdist/conf/type/__uci/files/functions.sh b/cdist/conf/type/__uci/files/functions.sh new file mode 100644 index 00000000..277f648c --- /dev/null +++ b/cdist/conf/type/__uci/files/functions.sh @@ -0,0 +1,73 @@ +# -*- mode: sh; indent-tabs-mode: t -*- + +in_list() { + printf '%s\n' "$@" | { grep -qxF "$(read -r ndl; echo "${ndl}")"; } +} + +quote() { + for _arg + do + shift + if test -n "$(printf %s "${_arg}" | tr -d -c '\t\n \042-\047\050-\052\073-\077\133\\`|~' | tr -c '' '.')" + then + # needs quoting + set -- "$@" "$(printf "'%s'" "$(printf %s "${_arg}" | sed -e "s/'/'\\\\''/g")")" + else + set -- "$@" "${_arg}" + fi + done + unset _arg + + # NOTE: Use printf because POSIX echo interprets escape sequences + printf '%s' "$*" +} + +uci_cmd() { + # Usage: uci_cmd [UCI ARGUMENTS]... + mkdir -p "${__object:?}/files" + printf '%s\n' "$(quote "$@")" >>"${__object:?}/files/uci_batch.txt" +} + +uci_validate_name() { + # like util.c uci_validate_name() + test -n "$*" && test -z "$(echo "$*" | tr -d '[:alnum:]_')" +} + +uci_validate_tuple() ( + tok=${1:?} + case $tok + in + (*.*.*) + # check option + option=${tok##*.} + uci_validate_name "${option}" || { + printf 'Invalid option: %s\n' "${option}" >&2 + return 1 + } + tok=${tok%.*} + ;; + (*.*) + # no option (section definition) + ;; + (*) + printf 'Invalid tuple: %s\n' "$1" >&2 + return 1 + ;; + esac + + case ${tok#*.} + in + (@*) section=$(expr "${tok#*.}" : '@\(.*\)\[-*[0-9]*\]$') ;; + (*) section=${tok#*.} ;; + esac + uci_validate_name "${section}" || { + printf 'Invalid section: %s\n' "${1#*.}" >&2 + return 1 + } + + config=${tok%%.*} + uci_validate_name "${config}" || { + printf 'Invalid config: %s\n' "${config}" >&2 + return 1 + } +) diff --git a/cdist/conf/type/__uci/files/uci_apply.sh b/cdist/conf/type/__uci/files/uci_apply.sh new file mode 100644 index 00000000..63f94290 --- /dev/null +++ b/cdist/conf/type/__uci/files/uci_apply.sh @@ -0,0 +1,43 @@ +changes=$(uci changes) + +if test -n "${changes}" +then + echo 'Uncommited UCI changes were found on the target:' + printf '%s\n\n' "${changes}" + echo 'This can be caused by manual changes or due to a previous failed run.' + echo 'Please investigate the situation, revert or commit the changes, and try again.' + exit 1 +fi >&2 + +check_errors() { + # reads stdin and forwards non-empty lines to stderr. + # returns 0 if stdin is empty, else 1. + ! grep -e . >&2 +} + +commit() { + uci commit +} + +rollback() { + printf '\nAn error occurred when trying to commit UCI transaction!\n' >&2 + + uci changes \ + | sed -e 's/^-//' -e 's/\..*\$//' \ + | sort -u \ + | while read -r _package + do + uci revert "${_package}" + echo "${_package}" # for logging + done \ + | awk ' + BEGIN { printf "Reverted changes in: " } + { printf "%s%s", (FNR > 1 ? ", " : ""), $0 } + END { printf "\n" }' >&2 + + return 1 +} + +uci_apply() { + uci batch 2>&1 | check_errors && commit || rollback +} diff --git a/cdist/conf/type/__uci/gencode-remote b/cdist/conf/type/__uci/gencode-remote new file mode 100755 index 00000000..70a3d3e0 --- /dev/null +++ b/cdist/conf/type/__uci/gencode-remote @@ -0,0 +1,101 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +# shellcheck source=cdist/conf/type/__uci/files/functions.sh +. "${__type:?}/files/functions.sh" + +state_is=$(cat "${__object:?}/explorer/state") +state_should=$(cat "${__object:?}/parameter/state") + +config=${__object_id:?} +uci_validate_tuple "${config}" + + +case ${state_should} +in + (present) + if in_list "${state_is}" 'present' 'rearranged' + then + # NOTE: order is ignored so rearranged is also fine. + exit 0 + fi + + # Determine type + type=$(cat "${__object:?}/parameter/type" 2>/dev/null || true) + case ${type} + in + (option|list) ;; + ('') + # Guess type by the number of values + test "$(wc -l "${__object:?}/parameter/value")" -gt 1 \ + && type=list \ + || type=option + ;; + (*) + printf 'Invalid --type: %s\n' "${type}" >&2 + exit 1 + ;; + esac + + case ${type} + in + (list) + printf 'set_list %s\n' "${config}" >>"${__messages_out:?}" + + if test "${state_is}" != 'absent' + then + uci_cmd delete "${config}" + fi + + while read -r value + do + uci_cmd add_list "${config}"="${value}" + done <"${__object:?}/parameter/value" + ;; + (option) + printf 'set %s\n' "${config}" >>"${__messages_out:?}" + + value=$(cat "${__object:?}/parameter/value") + uci_cmd set "${config}"="${value}" + ;; + esac + ;; + (absent) + if in_list "${state_is}" 'absent' + then + exit 0 + fi + + printf 'delete %s\n' "${config}" >>"${__messages_out:?}" + uci_cmd delete "${config}" + ;; + (*) + printf 'Invalid --state: %s\n' "${state_should}" >&2 + exit 1 + ;; +esac + +if test -s "${__object:?}/files/uci_batch.txt" +then + cat "${__type:?}/files/uci_apply.sh" + printf "uci_apply <<'EOF'\n" + cat "${__object:?}/files/uci_batch.txt" + printf '\nEOF\n' +fi diff --git a/cdist/conf/type/__uci/man.rst b/cdist/conf/type/__uci/man.rst new file mode 100644 index 00000000..81a53473 --- /dev/null +++ b/cdist/conf/type/__uci/man.rst @@ -0,0 +1,78 @@ +cdist-type__uci(7) +================== + +NAME +---- +cdist-type__uci - Manage configuration values in UCI + + +DESCRIPTION +----------- +This cdist type can be used to alter configuration options in OpenWrt's +Unified Configuration Interface (UCI) system. + + +REQUIRED PARAMETERS +------------------- +value + The value to be set. Can be used multiple times. + This parameter is ignored if ``--state`` is ``absent``. + + Due to the way cdist handles arguments, values **must not** contain newline + characters. + + Values do not need special quoting for UCI. The only requirement is that the + value is passed to the type as a single shell argument. + +OPTIONAL PARAMETERS +------------------- +state + ``present`` or ``absent``, defaults to ``present``. +type + If the type should generate an option or a list. + One of: ``option`` or ``list``. + Defaults to auto-detect based on the number of ``--value`` parameters. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Set the system hostname + __uci system.@system[0].hostname --value 'OpenWrt' + + # Set DHCP option 252: tell DHCP clients to not ask for proxy information. + __uci dhcp.lan.dhcp_option --type list --value '252,"\n"' + + # Enable NTP and NTPd (each is applied individually) + __uci system.ntp.enabled --value 1 + __uci system.ntp.enable_server --value 1 + __uci system.ntp.server --type list \ + --value '0.openwrt.pool.ntp.org' \ + --value '1.openwrt.pool.ntp.org' \ + --value '2.openwrt.pool.ntp.org' \ + --value '3.openwrt.pool.ntp.org' + + +SEE ALSO +-------- +- https://openwrt.org/docs/guide-user/base-system/uci + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. You can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. diff --git a/cdist/conf/type/__uci/manifest b/cdist/conf/type/__uci/manifest new file mode 100755 index 00000000..26920011 --- /dev/null +++ b/cdist/conf/type/__uci/manifest @@ -0,0 +1,51 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +os=$(cat "${__global:?}/explorer/os") + +state_should=$(cat "${__object:?}/parameter/state") + +case ${os} +in + (openwrt) + # okay + ;; + (*) + printf "Your operating system (%s) is currently not supported by this type (%s)\n" "${os}" "${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + ;; +esac + +case ${state_should} +in + (present) + test -s "${__object:?}/parameter/value" || { + echo 'The parameter --value is required.' >&2 + exit 1 + } + ;; + (absent) + ;; + (*) + printf 'Invalid --state: %s\n' "${state_should}" >&2 + exit 1 + ;; +esac diff --git a/cdist/conf/type/__uci/nonparallel b/cdist/conf/type/__uci/nonparallel new file mode 100644 index 00000000..e69de29b diff --git a/cdist/conf/type/__uci/parameter/default/state b/cdist/conf/type/__uci/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__uci/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__uci/parameter/optional b/cdist/conf/type/__uci/parameter/optional new file mode 100644 index 00000000..d9080e3a --- /dev/null +++ b/cdist/conf/type/__uci/parameter/optional @@ -0,0 +1,2 @@ +state +type diff --git a/cdist/conf/type/__uci/parameter/optional_multiple b/cdist/conf/type/__uci/parameter/optional_multiple new file mode 100644 index 00000000..6d4e1507 --- /dev/null +++ b/cdist/conf/type/__uci/parameter/optional_multiple @@ -0,0 +1 @@ +value diff --git a/cdist/conf/type/__uci_section/explorer/match b/cdist/conf/type/__uci_section/explorer/match new file mode 100644 index 00000000..0768e404 --- /dev/null +++ b/cdist/conf/type/__uci_section/explorer/match @@ -0,0 +1,103 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer determines the "prefix" of the --type section matching --match +# if set, or __object_id otherwise. + +RS=$(printf '\036') +NL=$(printf '\n '); NL=${NL% } + +squote_values() { + sed -e '/=".*"$/{s/="/='\''/;s/"$/'\''/}' \ + -e "/='.*'$/"'!{s/=/='\''/;s/$/'\''/}' +} +count_lines() ( + IFS=${NL?} + # shellcheck disable=SC2048,SC2086 + set -f -- $*; echo $# +) + +echo "${__object_id:?}" | grep -q -e '^[^.]\{1,\}\.[^.]\{1,\}$' || { + echo 'Section identifiers are a package and section name separated by a "." (period).' >&2 + exit 1 +} + +test -s "${__object:?}/parameter/match" || { + # If no --match is given, we take the __object_id as the section identifier. + echo "${__object_id:?}" + exit 0 +} +test -s "${__object:?}/parameter/type" || { + echo 'Parameters --match and --type must be used together.' >&2 + exit 1 +} + +sect_type_param=$(cat "${__object:?}/parameter/type") +expr "${sect_type_param}" : '[^.]\{1,\}\.[^.]\{1,\}$' >/dev/null 2>&1 || { + echo 'Section types are a package name and section type separated by a "." (period).' >&2 + exit 1 +} +package_filter=${sect_type_param%%.*} +section_filter=${sect_type_param#*.} + +# Find by --match +# NOTE: Apart from section types all values are printed in single quotes by uci show. +match=$(head -n 1 "${__object:?}/parameter/match" | squote_values) + +if uci -s -N get "${__object_id:?}" >/dev/null 2>&1 +then + # Named section exists: ensure if --match applies to it + # if the "matched" option does not exist (e.g. empty section) we use the + # section unconditionally. + if match_value_is=$(uci -s -N get "${__object_id:?}.${match%%=*}" 2>/dev/null) + then + match_value_should=$(expr "${match}" : ".*='\\(.*\\)'$") + + test "${match_value_is}" = "${match_value_should}" || { + printf 'Named section "%s" does not match --match "%s"\n' \ + "${__object_id:?}" "${match}" >&2 + exit 1 + } + fi + + echo "${__object_id:?}" + exit 0 +fi + +# No correctly named section exists already: find one to which --match applies +regex="^${package_filter}\\.@${section_filter}\\[[0-9]\\{1,\\}\\]\\.${match%%=*}=" + +matched_sections=$( + uci -s -N -d "${RS}" show "${package_filter}" 2>/dev/null \ + | grep -e "${regex}" \ + | while read -r _line + do + if test "${_line#*=}" = "${match#*=}" + then + echo "${_line}" + fi + done \ + | sed -e 's/\.[^.]*=.*$//') + +test "$(count_lines "${matched_sections}")" -le 1 || { + printf 'Found multiple matching sections:\n%s\n' "${matched_sections}" >&2 + exit 1 +} + +echo "${matched_sections}" diff --git a/cdist/conf/type/__uci_section/explorer/options b/cdist/conf/type/__uci_section/explorer/options new file mode 100644 index 00000000..e1e60668 --- /dev/null +++ b/cdist/conf/type/__uci_section/explorer/options @@ -0,0 +1,48 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer retrieves the current options of the configuration section. + +RS=$(printf '\036') + +section=$("${__type_explorer:?}/match") +test -n "${section}" || exit 0 + +uci -s -N -d "${RS}" show "${section}" 2>/dev/null \ +| awk -v VSEP="${RS}" ' + { + # Strip off the config and section parts + is_opt = sub(/^([^.]*\.){2}/, "") + + if (!is_opt) { + # this line represents the section -> skip + next + } + + if (index($0, VSEP)) { + # Put values each on a line, like --option and --list parameters + opt = substr($0, 1, index($0, "=") - 1) + split(substr($0, length(opt) + 2), values, VSEP) + for (i in values) { + printf "%s=%s\n", opt, values[i] + } + } else { + print + } + }' diff --git a/cdist/conf/type/__uci_section/explorer/type b/cdist/conf/type/__uci_section/explorer/type new file mode 100644 index 00000000..1675c2e0 --- /dev/null +++ b/cdist/conf/type/__uci_section/explorer/type @@ -0,0 +1,25 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# +# This explorer retrieves the current section type. + +section=$("${__type_explorer:?}/match") +test -n "${section}" || exit 0 + +uci -s -N get "${section}" 2>/dev/null || true diff --git a/cdist/conf/type/__uci_section/files/functions.sh b/cdist/conf/type/__uci_section/files/functions.sh new file mode 100644 index 00000000..60cb9148 --- /dev/null +++ b/cdist/conf/type/__uci_section/files/functions.sh @@ -0,0 +1,59 @@ +# -*- mode: sh; indent-tabs-mode: t -*- + +NL=$(printf '\n '); NL=${NL% } + +grep_line() { + { shift; printf '%s\n' "$@"; } | grep -qxF "$1" +} + +print_errors() { + awk -v prefix="${1:-Found errors:}" -v suffix="${2-}" ' + BEGIN { + if (getline) { + print prefix + print + rc = 1 + } + } + { print } + END { + if (rc && suffix) print suffix + exit rc + }' >&2 +} + +quote() { + for _arg + do + shift + if test -n "$(printf %s "${_arg}" | tr -d -c '\t\n \042-\047\050-\052\073-\077\133\\`|~' | tr -c '' '.')" + then + # needs quoting + set -- "$@" "$(printf "'%s'" "$(printf %s "${_arg}" | sed -e "s/'/'\\\\''/g")")" + else + set -- "$@" "${_arg}" + fi + done + unset _arg + printf '%s' "$*" +} + +uci_cmd() { + # Usage: uci_cmd [UCI ARGUMENTS]... + mkdir -p "${__object:?}/files" + printf '%s\n' "$(quote "$@")" >>"${__object:?}/files/uci_batch.txt" +} + +uci_validate_name() { + # like util.c uci_validate_name() + test -n "$*" && test -z "$(printf %s "$*" | tr -d '[:alnum:]_' | tr -c '' .)" +} + +unquote_lines() { + sed -e '/^".*"$/{s/^"//;s/"$//}' \ + -e '/'"^'.*'"'$/{s/'"^'"'//;s/'"'$"'//}' +} + +validate_options() { + grep -shv -e '^[[:alnum:]_]\{1,\}=' "$@" +} diff --git a/cdist/conf/type/__uci_section/files/option_state.awk b/cdist/conf/type/__uci_section/files/option_state.awk new file mode 100644 index 00000000..97cd94fb --- /dev/null +++ b/cdist/conf/type/__uci_section/files/option_state.awk @@ -0,0 +1,91 @@ +# -*- mode: awk; indent-tabs-mode:t -*- +# Usage: awk -f option_state.awk option_type option_name +# e.g. awk -f option_state.awk option title +# awk -f option_state.awk list entry + +function unquote(s) { + # simplified dequoting of single quoted strings + if (s ~ /^'.*'$/) { + s = substr(s, 2, length(s) - 2) + sub(/'\\''/, "'", s) + } + return s +} + +function valueof(line) { + if (line !~ /^[[:alpha:]_]+=/) return 0 + return unquote(substr(line, index(line, "=") + 1)) +} + +BEGIN { + __object = ENVIRON["__object"] + if (!__object) exit 1 + + opttype = ARGV[1] + optname = ARGV[2] + + if (opttype !~ /^(option|list)/ || !optname) { + print "invalid" + exit (e=1) + } + + ARGV[1] = __object "/parameter/" opttype + ARGV[2] = __object "/explorer/options" + + state = "present" +} + +NR == FNR { + # memoize "should" state + if (index($0, optname "=") == 1) { + should[++should_count] = valueof($0) + } + + # go to next line (important!) + next +} + +{ + # compare "is" state + if (index($0, optname "=") != 1) + next + ++is_count + + v = valueof($0) + + if (v == should[is_count]) { + # looks good, but can't say definitely just from this line + } else if (is_count > should_count) { + # there are more "is" records than "should" -> definitely different + state = "different" + exit + } else { + # see if we can find the "is" value somewhere in "should" + for (i in should) { + if (v == should[i]) { + # value found -> could be rearranged + # FIXME: Duplicate values are not properly handled here. Do they matter? + state = "rearranged" + next + } + } + + # "is" value could not be found in "should" -> definitely different + state = "different" + exit + } +} + +END { + if (e) exit + + if (!is_count) { + # no "is" values -> absent + state = "absent" + } else if (is_count < should_count) { + # "is" was shorter than "should" -> different + state = "different" + } + + print state +} diff --git a/cdist/conf/type/__uci_section/files/uci_apply.sh b/cdist/conf/type/__uci_section/files/uci_apply.sh new file mode 120000 index 00000000..4209151f --- /dev/null +++ b/cdist/conf/type/__uci_section/files/uci_apply.sh @@ -0,0 +1 @@ +../../__uci/files/uci_apply.sh \ No newline at end of file diff --git a/cdist/conf/type/__uci_section/gencode-remote b/cdist/conf/type/__uci_section/gencode-remote new file mode 100755 index 00000000..50fdfa4e --- /dev/null +++ b/cdist/conf/type/__uci_section/gencode-remote @@ -0,0 +1,174 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# This file is part of cdist. +# +# cdist is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cdist is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with cdist. If not, see . +# + +# shellcheck source=cdist/conf/type/__uci_section/files/functions.sh +. "${__type:?}/files/functions.sh" + + +section=$(cat "${__object:?}/explorer/match") + +state_is=$(test -s "${__object:?}/explorer/type" && echo present || echo absent) +state_should=$(cat "${__object:?}/parameter/state") + +case $state_should +in + (present) + test -f "${__object:?}/parameter/type" || { + echo 'Parameter --type is required.' >&2 + exit 1 + } + + type_is=$(cat "${__object:?}/explorer/type") + type_should=$(cat "${__object:?}/parameter/type") + + if test -n "${type_is}" + then + sect_type=${type_is} + else + sect_type=${type_should##*.} + fi + + if test -z "${section}" + then + # No section exists and --match was used. + # So we generate a new section identifier from $__object_id. + case ${__object_id:?} + in + (*.*) section=${__object_id:?} ;; + (*) section="${type_should%%.*}.${__object_id:?}" ;; + esac + fi + + # Collect option names + if test -f "${__object:?}/parameter/list" + then + listnames_should=$( + sed -e 's/=.*$//' "${__object:?}/parameter/list" | sort -u) + fi + + if test -f "${__object:?}/parameter/option" + then + optnames_should=$( + sed -e 's/=.*$//' "${__object:?}/parameter/option" | sort -u) + fi + + # Make sure the section itself is present + if test "${state_is}" = absent \ + || test "${type_is}" != "${type_should#*.}" + then + printf 'set %s\n' "${section}" >>"${__messages_out:?}" + # shellcheck disable=SC2140 + uci_cmd set "${section}"="${sect_type}" + fi + + # Delete options/lists not in "should" + sed -e 's/=.*$//' "${__object:?}/explorer/options" \ + | while read -r _optname + do + grep_line "${_optname}" "${listnames_should}" "${optnames_should}" || { + printf 'delete %s\n' "${section}.${_optname}" >>"${__messages_out:?}" + uci_cmd delete "${section}.${_optname}" + } &2 + exit 1 + } + + # Set "should" options + echo "${optnames_should}" \ + | grep -e . \ + | while read -r _optname + do + _opt_state=$(awk -f "${__type:?}/files/option_state.awk" option "${_optname}") \ + || opt_proc_error "${_optname}" + case ${_opt_state} + in + (invalid) + opt_proc_error "${_optname}" + ;; + (present) + ;; + (*) + printf 'set %s\n' "${section}.${_optname}" >>"${__messages_out:?}" + + # shellcheck disable=SC2140 + uci_cmd set "${section}.${_optname}"="$( + grep -e "^${_optname}=" "${__object:?}/parameter/option" \ + | sed -e 's/^.*=//' \ + | unquote_lines \ + | head -n 1)" + ;; + esac + done + + echo "${listnames_should}" \ + | grep -e . \ + | while read -r _optname + do + _list_state=$(awk -f "${__type:?}/files/option_state.awk" list "${_optname}") \ + || opt_proc_error "${_optname}" + case ${_list_state} + in + (invalid) + opt_proc_error "${_optname}" + ;; + (present) + ;; + (*) + printf 'set_list %s\n' "${section}.${_optname}" >>"${__messages_out:?}" + + if test "${_list_state}" != absent + then + uci_cmd delete "${section}.${_optname}" + fi + + grep "^${_optname}=" "${__object:?}/parameter/list" \ + | sed -e 's/^.*=//' \ + | unquote_lines \ + | while read -r _value + do + # shellcheck disable=SC2140 + uci_cmd add_list "${section}.${_optname}"="${_value}" + done + ;; + esac + done + ;; + (absent) + if test "${state_is}" = absent + then + # if explorer found no section there is nothing to delete + exit 0 + fi + + printf 'delete %s\n' "${section}" >>"${__messages_out:?}" + uci_cmd delete "${section}" + ;; +esac + +if test -s "${__object:?}/files/uci_batch.txt" +then + cat "${__type:?}/files/uci_apply.sh" + printf "uci_apply <<'EOF'\n" + cat "${__object:?}/files/uci_batch.txt" + printf '\nEOF\n' +fi diff --git a/cdist/conf/type/__uci_section/man.rst b/cdist/conf/type/__uci_section/man.rst new file mode 100644 index 00000000..a0ab78e8 --- /dev/null +++ b/cdist/conf/type/__uci_section/man.rst @@ -0,0 +1,119 @@ +cdist-type__uci_section(7) +========================== + +NAME +---- +cdist-type__uci_section - Manage configuration sections in UCI + + +DESCRIPTION +----------- +This cdist type can be used to replace whole configuration sections in OpenWrt's +Unified Configuration Interface (UCI) system. +It can be thought of as syntactic sugar for :strong:`cdist-type__uci`\ (7), +as this type will generate the required `__uci` objects to make the section +contain exactly the options specified using ``--option``. + +Since many default UCI sections are unnamed, this type allows to find the +matching section by one of its options using the ``--match`` parameter. + +**NOTE:** Options already present on the target and not listed in ``--option`` +or ``--list`` will be deleted. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +list + An option that is part of a list and should be present in the section (as + part of a list). Lists with multiple options can be expressed by using the + same ``