Initial commit
This commit is contained in:
parent
bc99df3659
commit
dbfef934d6
12 changed files with 10452 additions and 1 deletions
35
Containerfile
Normal file
35
Containerfile
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
FROM registry.fedoraproject.org/fedora:39
|
||||
|
||||
LABEL org.opencontainers.image.authors="Fedora Cloud SIG <cloud@lists.fedoraproject.org"
|
||||
|
||||
USER root
|
||||
RUN dnf install -y \
|
||||
patch \
|
||||
python3-pip \
|
||||
python3-hatchling
|
||||
|
||||
RUN useradd -m -d /srv/cloud-uploader cloud-uploader
|
||||
COPY . /srv/cloud-uploader/src
|
||||
RUN chown -R cloud-uploader:cloud-uploader /srv/cloud-uploader/src
|
||||
|
||||
USER cloud-uploader
|
||||
WORKDIR /srv/cloud-uploader
|
||||
RUN python3 -m venv venv && \
|
||||
venv/bin/pip install --no-cache-dir src/ && \
|
||||
venv/bin/ansible-galaxy collection install azure.azcollection:2.2.0 && \
|
||||
venv/bin/pip install -r .ansible/collections/ansible_collections/azure/azcollection/requirements-azure.txt
|
||||
|
||||
# These are not yet in an upstream release, when azure.azcollection > 2.2.0 check applicability
|
||||
# https://github.com/ansible-collections/azure/pull/1466
|
||||
# https://github.com/ansible-collections/azure/pull/1493
|
||||
RUN pushd .ansible/collections/ansible_collections/azure/azcollection/ && \
|
||||
patch -p1 < ~/src/patches/azure-pr-1466.patch && \
|
||||
patch -p1 < ~/src/patches/azure-pr-1493.patch && \
|
||||
popd
|
||||
|
||||
# ansible-runner shells out so the PATH needs to include the virtualenv
|
||||
ENV PATH="/srv/cloud-uploader/venv/bin:$PATH"
|
||||
|
||||
# Add the virtual environment to the path
|
||||
ENTRYPOINT ["/srv/cloud-uploader/venv/bin/fedora-messaging"]
|
||||
CMD ["consume", "--callback=fedora_cloud_image_uploader:consumer"]
|
||||
339
LICENSE
Normal file
339
LICENSE
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) 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
|
||||
this service 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 make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. 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.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
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
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE 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.
|
||||
|
||||
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
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 2 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, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision 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, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This 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.
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
# cloud-image-uploader
|
||||
|
||||
An AMQP consumer that automatically uploads Cloud images to their respective homes.
|
||||
An AMQP consumer that automatically uploads Cloud images to their respective homes.
|
||||
|
|
|
|||
98
fedora-messaging.toml.example
Normal file
98
fedora-messaging.toml.example
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# A sample configuration for fedora-messaging. This file is in the TOML format.
|
||||
amqp_url = "amqp://"
|
||||
callback = "fedora_cloud_image_uploader:consumer"
|
||||
passive_declares = false
|
||||
publish_exchange = "amq.topic"
|
||||
topic_prefix = ""
|
||||
|
||||
[tls]
|
||||
ca_cert = "/etc/fedora-messaging/cacert.pem"
|
||||
keyfile = "/etc/fedora-messaging/fedora-key.pem"
|
||||
certfile = "/etc/fedora-messaging/fedora-cert.pem"
|
||||
|
||||
[client_properties]
|
||||
app = "Fedora Cloud Image Uploader"
|
||||
app_url = "https://pagure.io/cloud-image-uploader"
|
||||
app_contacts_email = "cloud@lists.fedoraproject.org"
|
||||
|
||||
# If the exchange or queue name has a "." in it, use quotes as seen here.
|
||||
[exchanges."amq.topic"]
|
||||
type = "topic"
|
||||
durable = true
|
||||
auto_delete = false
|
||||
arguments = {}
|
||||
|
||||
[queues.my_queue]
|
||||
durable = true
|
||||
auto_delete = false
|
||||
exclusive = false
|
||||
arguments = {}
|
||||
|
||||
# Note the double brackets below. To add another binding, add another
|
||||
# [[bindings]] section. To use multiple routing keys, just expand the list here.
|
||||
[[bindings]]
|
||||
queue = "my_queue"
|
||||
exchange = "amq.topic"
|
||||
routing_keys = ["org.fedoraproject.prod.buildsys.build.state.change"]
|
||||
|
||||
[consumer_config.azure]
|
||||
location = "eastus2"
|
||||
resource_group_name = "fedora-rg"
|
||||
gallery_name = "Fedora"
|
||||
gallery_description = "The Fedora compute gallery."
|
||||
storage_account_name = "fedoraimageuploads"
|
||||
storage_container_name = "vhds"
|
||||
|
||||
[[consumer_config.azure.target_regions]]
|
||||
name = "eastus2"
|
||||
regional_replica_count = 2
|
||||
storage_account_type = "Standard_LRS"
|
||||
|
||||
[[consumer_config.azure.target_regions]]
|
||||
name = "centralus"
|
||||
regional_replica_count = 2
|
||||
storage_account_type = "Standard_LRS"
|
||||
|
||||
[qos]
|
||||
prefetch_size = 0
|
||||
prefetch_count = 25
|
||||
|
||||
[log_config]
|
||||
version = 1
|
||||
disable_existing_loggers = true
|
||||
|
||||
[log_config.formatters.simple]
|
||||
format = "[%(levelname)s %(name)s] %(message)s"
|
||||
|
||||
[log_config.handlers.console]
|
||||
class = "logging.StreamHandler"
|
||||
formatter = "simple"
|
||||
stream = "ext://sys.stdout"
|
||||
|
||||
[log_config.loggers.fedora_cloud_image_uploader]
|
||||
level = "INFO"
|
||||
propagate = false
|
||||
handlers = ["console"]
|
||||
|
||||
[log_config.loggers.fedora_messaging]
|
||||
level = "INFO"
|
||||
propagate = false
|
||||
handlers = ["console"]
|
||||
|
||||
# Twisted is the asynchronous framework that manages the TCP/TLS connection, as well
|
||||
# as the consumer event loop. When debugging you may want to lower this log level.
|
||||
[log_config.loggers.twisted]
|
||||
level = "INFO"
|
||||
propagate = false
|
||||
handlers = ["console"]
|
||||
|
||||
# Pika is the underlying AMQP client library. When debugging you may want to
|
||||
# lower this log level.
|
||||
[log_config.loggers.pika]
|
||||
level = "WARNING"
|
||||
propagate = false
|
||||
handlers = ["console"]
|
||||
|
||||
[log_config.root]
|
||||
level = "ERROR"
|
||||
handlers = ["console"]
|
||||
6
fedora_cloud_image_uploader/__init__.py
Normal file
6
fedora_cloud_image_uploader/__init__.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import os
|
||||
|
||||
PLAYBOOKS = os.path.abspath(os.path.join(os.path.dirname(__file__), "playbooks/"))
|
||||
__version__ = "0.2.0"
|
||||
|
||||
from .handler import consumer
|
||||
14
fedora_cloud_image_uploader/cli.py
Normal file
14
fedora_cloud_image_uploader/cli.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import json
|
||||
|
||||
import click
|
||||
from fedora_messaging import message, config
|
||||
|
||||
from . import handler
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("message_file", type=click.File("r"))
|
||||
def main(message_file):
|
||||
config.conf.setup_logging()
|
||||
msg = message.load_message(json.load(message_file))
|
||||
handler.consumer(msg)
|
||||
163
fedora_cloud_image_uploader/handler.py
Normal file
163
fedora_cloud_image_uploader/handler.py
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
from urllib.parse import urlunsplit, urlsplit
|
||||
import tempfile
|
||||
import os
|
||||
import lzma
|
||||
import logging
|
||||
import hashlib
|
||||
|
||||
from fedora_messaging import config, exceptions as fm_exceptions
|
||||
from koji_fedoramessaging_messages import build
|
||||
from requests import Session
|
||||
from requests.exceptions import RequestException
|
||||
import ansible_runner
|
||||
|
||||
from . import PLAYBOOKS
|
||||
|
||||
session = Session()
|
||||
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def consumer(message: build.BuildStateChangeV1):
|
||||
"""
|
||||
Consumes Koji messages for build state changes
|
||||
|
||||
see https://apps.fedoraproject.org/datagrepper/v2/id?id=a0b33cca-25cd-411a-ad8f-7eb851b1496f&is_raw=true&size=extra-large
|
||||
"""
|
||||
conf = config.conf["consumer_config"]
|
||||
# Skip any messages that aren't about completed builds
|
||||
if message.new != build.BUILD_STATES.COMPLETE.value:
|
||||
return
|
||||
|
||||
if message.name == "Fedora-Cloud-Base-Azure":
|
||||
# "https://kojipkgs.fedoraproject.org/work"
|
||||
base_url = urlsplit(message.body["files_base_url"])
|
||||
# Could be a number or "Rawhide"
|
||||
version = message.version
|
||||
# Could be a YYYYMMDD.n.N where N is the build number for the day, or something like 1.3
|
||||
release = message.release
|
||||
|
||||
# TODO map Rawhide to a number via bodhi releases
|
||||
image_version = f"{version if version != 'Rawhide' else '41'}.{release.split('.')[0]}.{release.split('.')[-1]}"
|
||||
|
||||
_log.info(
|
||||
"Processing Azure image %s-%s which just completed in Koji",
|
||||
version,
|
||||
release,
|
||||
)
|
||||
for child_task in message.body["task"]["children"]:
|
||||
# Map Fedora arch names to Azure arch names
|
||||
match child_task["result"]["arch"]:
|
||||
case "x86_64":
|
||||
arch = "x64"
|
||||
case "aarch64":
|
||||
arch = "Arm64"
|
||||
case _:
|
||||
_log.error(
|
||||
"Unexpected architecture for Azure image: %s",
|
||||
child_task["result"]["arch"],
|
||||
)
|
||||
continue
|
||||
|
||||
(image_file_name, image_url) = [
|
||||
(
|
||||
file_name,
|
||||
urlunsplit(
|
||||
[
|
||||
base_url.scheme,
|
||||
base_url.netloc,
|
||||
f"/packages/{message.name}/{version}/{release}/images/{file_name}",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
),
|
||||
)
|
||||
for file_name in child_task["result"]["files"]
|
||||
if file_name.endswith("vhdfixed.xz")
|
||||
].pop()
|
||||
checksum_url = [
|
||||
urlunsplit(
|
||||
[
|
||||
base_url.scheme,
|
||||
base_url.netloc,
|
||||
f"/packages/{message.name}/{version}/{release}/images/{file_name}",
|
||||
"",
|
||||
"",
|
||||
]
|
||||
)
|
||||
for file_name in child_task["result"]["files"]
|
||||
if file_name.endswith("vhdfixed.xz.sha256")
|
||||
].pop()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
image_dest = os.path.join(tmpdir, image_file_name.removesuffix(".xz"))
|
||||
decompressor = lzma.LZMADecompressor()
|
||||
checksum = hashlib.sha256(usedforsecurity=False)
|
||||
|
||||
try:
|
||||
with session.get(image_url, stream=True, timeout=30) as req:
|
||||
_log.info(f"Writing {image_url} to {image_dest}")
|
||||
req.raise_for_status()
|
||||
with open(image_dest, "wb") as fd:
|
||||
for chunk in req.iter_content(chunk_size=16 * 1024):
|
||||
checksum.update(chunk)
|
||||
decompressed = decompressor.decompress(chunk)
|
||||
fd.write(decompressed)
|
||||
except RequestException as e:
|
||||
_log.error("Failed to download image from %s: %s", image_url, e)
|
||||
raise fm_exceptions.Nack()
|
||||
|
||||
actual_checksum = checksum.hexdigest()
|
||||
_log.info("Image downloaded (sha256:%s)", actual_checksum)
|
||||
if not decompressor.eof or decompressor.unused_data:
|
||||
_log.error(
|
||||
"LZMA unexpected state: EOF %s, unused bytes: %d",
|
||||
decompressor.eof,
|
||||
len(decompressor.unused_data),
|
||||
)
|
||||
raise fm_exceptions.Nack()
|
||||
|
||||
# Retrieve the checksum and compare it to what we downloaded.
|
||||
try:
|
||||
with session.get(checksum_url, timeout=30) as req:
|
||||
req.raise_for_status()
|
||||
expected_checksum = req.text.split()[0]
|
||||
except RequestException as e:
|
||||
_log.error(
|
||||
"Failed to download checksum from %s: %s", checksum_url, e
|
||||
)
|
||||
raise fm_exceptions.Nack()
|
||||
|
||||
if actual_checksum != expected_checksum:
|
||||
_log.error(
|
||||
"Image checksum (sha256:%s) does not match checksum from Koji (sha256:%s)",
|
||||
actual_checksum,
|
||||
expected_checksum,
|
||||
)
|
||||
raise fm_exceptions.Nack()
|
||||
|
||||
playbook = os.path.join(PLAYBOOKS, "azure.yml")
|
||||
_log.info("Executing playbook %s", playbook)
|
||||
# TODO:
|
||||
# Fetch EOL from bodhi releases endpoint
|
||||
# https://bodhi.fedoraproject.org/releases/?state=pending
|
||||
# https://bodhi.fedoraproject.org/releases/?state=current
|
||||
variables = {
|
||||
"location": conf["azure"]["location"],
|
||||
"resource_group_name": conf["azure"]["resource_group_name"],
|
||||
"gallery_name": conf["azure"]["gallery_name"],
|
||||
"gallery_description": conf["azure"]["gallery_description"],
|
||||
"storage_account_name": conf["azure"]["storage_account_name"],
|
||||
"storage_container_name": conf["azure"]["storage_container_name"],
|
||||
"target_regions": conf["azure"]["target_regions"],
|
||||
"architecture": arch,
|
||||
"image_source": image_dest,
|
||||
"gallery_image_name": version,
|
||||
"gallery_image_version": image_version,
|
||||
}
|
||||
# TODO use the async flavor to do all arches at once, see if redirecting stdout/err is possible
|
||||
runner = ansible_runner.interface.run(
|
||||
playbook=playbook,
|
||||
timeout=30 * 60,
|
||||
extravars=variables,
|
||||
)
|
||||
176
fedora_cloud_image_uploader/playbooks/azure.yml
Normal file
176
fedora_cloud_image_uploader/playbooks/azure.yml
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
# The user can either have already authenticated with the CLI (e.g. az login),
|
||||
# or provide credentials via environment variables. The following environment
|
||||
# variables are used by the azure collection:
|
||||
# AZURE_PROFILE
|
||||
# AZURE_SUBSCRIPTION_ID
|
||||
# AZURE_CLIENT_ID
|
||||
# AZURE_SECRET
|
||||
# AZURE_TENANT
|
||||
# AZURE_AD_USER
|
||||
# AZURE_PASSWORD
|
||||
# AZURE_CLOUD_ENVIRONMENT
|
||||
# AZURE_CERT_VALIDATION_MODE
|
||||
# AZURE_ADFS_AUTHORITY_URL
|
||||
# AZURE_X509_CERTIFICATE_PATH
|
||||
# AZURE_THUMBPRINT
|
||||
#
|
||||
# For example, if you added an app registration, created a client secret for it, and added it to a subscriptions
|
||||
# access control with enough priveleges ("Contributor" role is over-broad, but enough for testing) to your
|
||||
# subscription, you could provide the AZURE_SECRET, AZURE_TENANT, AZURE_CLIENT_ID, and AZURE_SUBSCRIPTION_ID
|
||||
# variables to authenticate.
|
||||
#
|
||||
# The caller must define the following variables:
|
||||
# architecture: The architecture of the image being uploaded; one of "x64" and "Arm64".
|
||||
# image_source: The path on the local filesystem where the image is stored.
|
||||
# gallery_image_name: Name of an image definition, which contains image versions.
|
||||
# gallery_image_version: Image version; must be in Major.Minor.Patch format, each within a 32-bit integer range.
|
||||
# end_of_life_date: ISO-8601 format date indicating when the operating system reaches end-of-life. Can be null.
|
||||
---
|
||||
- name: Create Fedora Azure marketplace image
|
||||
hosts: localhost
|
||||
vars:
|
||||
# The Azure Compute Gallery name.
|
||||
#
|
||||
# Must be letters, numbers, underscores, and periods. Cannot begin or end with underscores or periods.
|
||||
gallery_name: Fedora
|
||||
gallery_description: |
|
||||
The Fedora compute gallery.
|
||||
gallery_image_name: "40"
|
||||
# The image version; must be in Major.Minor.Patch format, each within a 32-bit integer range.
|
||||
gallery_image_version: "40.0.3"
|
||||
|
||||
# When the operating system goes end-of-life in ISO-8601 format.
|
||||
end_of_life_date: "2025-04-01"
|
||||
release_note_uri: "https://docs.fedoraproject.org/en-US/fedora/f40/release-notes/"
|
||||
privacy_statement_uri: "https://docs.fedoraproject.org/en-US/legal/privacy/"
|
||||
|
||||
# List of dictionaries describing the replication rules for each Azure region.
|
||||
target_regions:
|
||||
- name: eastus2
|
||||
regional_replica_count: 2
|
||||
# https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview#types-of-storage-accounts
|
||||
storage_account_type: Standard_LRS
|
||||
|
||||
# The Azure region all resources are deployed in.
|
||||
location: eastus2
|
||||
resource_group_name: fedora-ansible-test
|
||||
# Must be between 3 and 24 characters, numbers and lowercase letters only, globally unique
|
||||
#
|
||||
# https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview#storage-account-name
|
||||
storage_account_name: fedoraansibletest
|
||||
# Container for a collection of blobs.
|
||||
#
|
||||
# Must be lowercase letters, numbers, and hyphens. Must begin with a letter or number.
|
||||
# Must be between 3 and 63 characters long.
|
||||
storage_container_name: fedora-vhds
|
||||
|
||||
tasks:
|
||||
|
||||
- name: Create a resource group for everything
|
||||
azure.azcollection.azure_rm_resourcegroup:
|
||||
name: "{{ resource_group_name }}"
|
||||
location: "{{ location }}"
|
||||
state: present
|
||||
|
||||
- name: Create a storage account for image uploads
|
||||
azure.azcollection.azure_rm_storageaccount:
|
||||
resource_group: "{{ resource_group_name }}"
|
||||
name: "{{ storage_account_name }}"
|
||||
type: Standard_LRS
|
||||
kind: "StorageV2"
|
||||
|
||||
- name: Create a container in the storage account for images
|
||||
azure.azcollection.azure_rm_storageblob:
|
||||
resource_group: "{{ resource_group_name }}"
|
||||
storage_account_name: "{{ storage_account_name }}"
|
||||
container: "{{ storage_container_name }}"
|
||||
state: present
|
||||
|
||||
- name: Checksum local file
|
||||
ansible.builtin.stat:
|
||||
path: "{{ image_source }}"
|
||||
checksum_algorithm: sha256
|
||||
get_checksum: true
|
||||
register: local_image_file
|
||||
|
||||
- name: Set storage_blob_name to <sha256sum>.vhd
|
||||
ansible.builtin.set_fact:
|
||||
storage_blob_name: "{{ local_image_file.stat.checksum }}.vhd"
|
||||
|
||||
- name: Upload the image to blob storage
|
||||
azure.azcollection.azure_rm_storageblob:
|
||||
resource_group: "{{ resource_group_name }}"
|
||||
storage_account_name: "{{ storage_account_name }}"
|
||||
container: "{{ storage_container_name }}"
|
||||
blob_name: "{{ storage_blob_name }}"
|
||||
blob_type: page
|
||||
source: "{{ image_source }}"
|
||||
|
||||
# The ansible module doesn't include settings for sharing the gallery publicly
|
||||
# so for the real thing that probably should be done manually (once). This is
|
||||
# fine for local testing.
|
||||
- name: Ensure the gallery exists
|
||||
azure.azcollection.azure_rm_gallery:
|
||||
resource_group: "{{ resource_group_name }}"
|
||||
name: "{{ gallery_name }}"
|
||||
location: "{{ location }}"
|
||||
description: "{{ gallery_description }}"
|
||||
|
||||
- name: Create or update gallery image
|
||||
azure.azcollection.azure_rm_galleryimage:
|
||||
resource_group: "{{ resource_group_name }}"
|
||||
gallery_name: "{{ gallery_name }}"
|
||||
name: "{{ gallery_image_name }}-{{ architecture }}"
|
||||
release_note_uri: "{{ release_note_uri }}"
|
||||
privacy_statement_uri: "{{ privacy_statement_uri }}"
|
||||
location: "{{ location }}"
|
||||
hypervgeneration: "V2"
|
||||
architecture: "{{ architecture }}"
|
||||
os_type: linux
|
||||
os_state: generalized
|
||||
identifier:
|
||||
publisher: Fedora
|
||||
offer: Fedora
|
||||
sku: "{{ gallery_image_name }}-{{ architecture }}"
|
||||
recommended:
|
||||
memory:
|
||||
min: 1
|
||||
v_cpus:
|
||||
min: 1
|
||||
# See CONFIG_NR_CPUS
|
||||
max: 8192
|
||||
features:
|
||||
- name: SecurityType
|
||||
value: TrustedLaunchSupported
|
||||
- name: IsAcceleratedNetworkSupported
|
||||
value: true
|
||||
- name: DiskControllerTypes
|
||||
value: NVMe,SCSI
|
||||
|
||||
- name: Create a gallery image version from the VHD in blob storage
|
||||
azure.azcollection.azure_rm_galleryimageversion:
|
||||
resource_group: "{{ resource_group_name }}"
|
||||
gallery_name: "{{ gallery_name }}"
|
||||
gallery_image_name: "{{ gallery_image_name }}-{{ architecture }}"
|
||||
name: "{{ gallery_image_version }}"
|
||||
location: "{{ location }}"
|
||||
publishing_profile:
|
||||
end_of_life_date: "{{ end_of_life_date }}"
|
||||
exclude_from_latest: false
|
||||
target_regions: "{{ target_regions }}"
|
||||
storage_profile:
|
||||
os_disk:
|
||||
host_caching: "ReadOnly"
|
||||
source:
|
||||
resource_group: "{{ resource_group_name }}"
|
||||
storage_account: "{{ storage_account_name }}"
|
||||
uri: "https://{{ storage_account_name }}.blob.core.windows.net/{{ storage_container_name }}/{{ storage_blob_name }}"
|
||||
|
||||
# Once the image version is created, the copy in the storage account is unnecessary.
|
||||
- name: Clean up the VHD in blob storage
|
||||
azure.azcollection.azure_rm_storageblob:
|
||||
resource_group: "{{ resource_group_name }}"
|
||||
storage_account_name: "{{ storage_account_name }}"
|
||||
container: "{{ storage_container_name }}"
|
||||
blob_name: "{{ storage_blob_name }}"
|
||||
state: absent
|
||||
262
patches/azure-pr-1466.patch
Normal file
262
patches/azure-pr-1466.patch
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
From 8c34aeb78229d7487cb326d32de43e34d6cc6948 Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Cline <jeremycline@microsoft.com>
|
||||
Date: Mon, 26 Feb 2024 14:51:04 -0500
|
||||
Subject: [PATCH 1/3] Allow creating gallery image versions from storage
|
||||
accounts
|
||||
|
||||
It's possible to upload a VHD into a container in a storage account and
|
||||
then use that to import an image version. This adds a third way to
|
||||
specify an `os_disk` in the galleryimageversion plugin to support that
|
||||
approach.
|
||||
---
|
||||
.../modules/azure_rm_galleryimageversion.py | 41 ++++++++++++++++++-
|
||||
.../targets/azure_rm_gallery/tasks/main.yml | 40 ++++++++++++++++++
|
||||
2 files changed, 79 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/plugins/modules/azure_rm_galleryimageversion.py b/plugins/modules/azure_rm_galleryimageversion.py
|
||||
index 66a4dea6b..93bf031f1 100644
|
||||
--- a/plugins/modules/azure_rm_galleryimageversion.py
|
||||
+++ b/plugins/modules/azure_rm_galleryimageversion.py
|
||||
@@ -63,7 +63,10 @@
|
||||
suboptions:
|
||||
source:
|
||||
description:
|
||||
- - Reference to os disk snapshot. Could be resource ID or dictionary containing I(resource_group) and I(name)
|
||||
+ - Reference to os disk snapshot.
|
||||
+ - Could be resource ID.
|
||||
+ - Could be a dictionary containing I(resource_group) and I(name).
|
||||
+ - Could be a dictionary containing I(resource_group), I(storage_account), and I(uri) if the snapshot is stored as a PageBlob in a storage account container.
|
||||
type: raw
|
||||
host_caching:
|
||||
description:
|
||||
@@ -303,6 +306,28 @@
|
||||
name: data_snapshot_vma
|
||||
- lun: 1
|
||||
source: "/subscriptions/mySub/resourceGroups/myGroup/providers/Microsoft.Compute/snapshots/data_snapshot_vmb"
|
||||
+
|
||||
+- name: Create gallery image by using a os disk snapshot stored in Storage Account container
|
||||
+ azure_rm_galleryimageversion:
|
||||
+ resource_group: myResourceGroup
|
||||
+ gallery_name: myGallery
|
||||
+ gallery_image_name: myGalleryImage
|
||||
+ name: 3.4.0
|
||||
+ location: East US
|
||||
+ publishing_profile:
|
||||
+ end_of_life_date: "2020-10-01t00:00:00+00:00"
|
||||
+ exclude_from_latest: true
|
||||
+ replica_count: 1
|
||||
+ storage_account_type: Standard_LRS
|
||||
+ target_regions:
|
||||
+ - name: East US
|
||||
+ regional_replica_count: 1
|
||||
+ storage_account_type: Standard_LRS
|
||||
+ storage_profile:
|
||||
+ os_disk:
|
||||
+ resource_group: myResourceGroup
|
||||
+ storage_account: myStorageAccount
|
||||
+ uri: "https://myStorageAccount.blob.core.windows.net/myContainer/myImage.vhd"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
@@ -561,7 +586,7 @@ def exec_module(self, **kwargs):
|
||||
elif isinstance(kwargs[key]['os_disk']['source'], dict):
|
||||
if kwargs[key]['os_disk']['source'].get('id') is not None:
|
||||
self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = kwargs[key]['os_disk']['source'].get('id')
|
||||
- if kwargs[key]['os_disk']['source'].get('resource_group') is not None and \
|
||||
+ elif kwargs[key]['os_disk']['source'].get('resource_group') is not None and \
|
||||
kwargs[key]['os_disk']['source'].get('name') is not None:
|
||||
resource_group = kwargs[key]['os_disk']['source'].get('resource_group')
|
||||
self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = ('/subscriptions/' +
|
||||
@@ -570,6 +595,18 @@ def exec_module(self, **kwargs):
|
||||
resource_group +
|
||||
'/providers/Microsoft.Compute/snapshots/' +
|
||||
kwargs[key]['os_disk']['source'].get('name'))
|
||||
+ elif kwargs[key]['os_disk']['source'].get('uri') is not None and \
|
||||
+ kwargs[key]['os_disk']['source'].get('resource_group') is not None and \
|
||||
+ kwargs[key]['os_disk']['source'].get('storage_account') is not None:
|
||||
+ resource_group = kwargs[key]['os_disk']['source'].get('resource_group')
|
||||
+ storage_account = kwargs[key]['os_disk']['source'].get('storage_account')
|
||||
+ self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = ('/subscriptions/' +
|
||||
+ self.subscription_id +
|
||||
+ '/resourceGroups/' +
|
||||
+ resource_group +
|
||||
+ '/providers/Microsoft.Storage/storageAccounts/' +
|
||||
+ storage_account)
|
||||
+ self.body['properties']['storageProfile']['osDiskImage']['source']['uri'] = kwargs[key]['os_disk']['source'].get('uri')
|
||||
else:
|
||||
self.fail("The os_disk.source parameters config errors")
|
||||
|
||||
diff --git a/tests/integration/targets/azure_rm_gallery/tasks/main.yml b/tests/integration/targets/azure_rm_gallery/tasks/main.yml
|
||||
index 8270b04ce..0ebbe439f 100644
|
||||
--- a/tests/integration/targets/azure_rm_gallery/tasks/main.yml
|
||||
+++ b/tests/integration/targets/azure_rm_gallery/tasks/main.yml
|
||||
@@ -343,6 +343,46 @@
|
||||
that:
|
||||
- output.changed
|
||||
|
||||
+- name: Create or update a simple gallery Image Version.
|
||||
+ azure_rm_galleryimageversion:
|
||||
+ resource_group: "{{ resource_group }}"
|
||||
+ gallery_name: myGallery{{ rpfx }}
|
||||
+ gallery_image_name: myImage
|
||||
+ name: 10.1.3
|
||||
+ location: eastus
|
||||
+ publishing_profile:
|
||||
+ end_of_life_date: "2050-10-01t00:00:00+00:00"
|
||||
+ exclude_from_latest: true
|
||||
+ replica_count: 3
|
||||
+ storage_account_type: Standard_LRS
|
||||
+ target_regions:
|
||||
+ - name: eastus
|
||||
+ regional_replica_count: 1
|
||||
+ encryption:
|
||||
+ data_disk_images:
|
||||
+ - disk_encryption_set_id: "{{ des_results.state.id }}"
|
||||
+ os_disk_image:
|
||||
+ disk_encryption_set_id: "{{ des_results.state.id }}"
|
||||
+ - name: westus
|
||||
+ regional_replica_count: 2
|
||||
+ encryption:
|
||||
+ data_disk_images:
|
||||
+ - disk_encryption_set_id: "{{ des_results.state.id }}"
|
||||
+ os_disk_image:
|
||||
+ disk_encryption_set_id: "{{ des_results.state.id }}"
|
||||
+ storage_account_type: Standard_ZRS
|
||||
+ storage_profile:
|
||||
+ source_image:
|
||||
+ resource_group: "{{ resource_group }}"
|
||||
+ storage_account: '{{ output.vms[0].storage_account_name }}'
|
||||
+ uri: 'https://{{ output.vms[0].storage_account_name }}.blob.core.windows.net/{{ output.vms[0].storage_container_name }}/{{ output.vms[0].storage_blob_name }}'
|
||||
+ register: output
|
||||
+
|
||||
+- name: Assert the gallery image version created
|
||||
+ ansible.builtin.assert:
|
||||
+ that:
|
||||
+ - output.changed
|
||||
+
|
||||
- name: Create or update a simple gallery Image Version - idempotent
|
||||
azure_rm_galleryimageversion:
|
||||
resource_group: "{{ resource_group }}"
|
||||
|
||||
From 611ab25a9c323ff31e3f904e7860cc94e0106313 Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Cline <jeremycline@microsoft.com>
|
||||
Date: Wed, 28 Feb 2024 09:35:11 -0500
|
||||
Subject: [PATCH 2/3] Address PR comments for style
|
||||
|
||||
There's no functional change.
|
||||
---
|
||||
plugins/modules/azure_rm_galleryimageversion.py | 12 +++++++-----
|
||||
1 file changed, 7 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/plugins/modules/azure_rm_galleryimageversion.py b/plugins/modules/azure_rm_galleryimageversion.py
|
||||
index 93bf031f1..cebad20b3 100644
|
||||
--- a/plugins/modules/azure_rm_galleryimageversion.py
|
||||
+++ b/plugins/modules/azure_rm_galleryimageversion.py
|
||||
@@ -66,7 +66,8 @@
|
||||
- Reference to os disk snapshot.
|
||||
- Could be resource ID.
|
||||
- Could be a dictionary containing I(resource_group) and I(name).
|
||||
- - Could be a dictionary containing I(resource_group), I(storage_account), and I(uri) if the snapshot is stored as a PageBlob in a storage account container.
|
||||
+ - Could be a dictionary containing I(resource_group), I(storage_account), and I(uri)
|
||||
+ if the snapshot is stored as a PageBlob in a storage account container.
|
||||
type: raw
|
||||
host_caching:
|
||||
description:
|
||||
@@ -587,7 +588,7 @@ def exec_module(self, **kwargs):
|
||||
if kwargs[key]['os_disk']['source'].get('id') is not None:
|
||||
self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = kwargs[key]['os_disk']['source'].get('id')
|
||||
elif kwargs[key]['os_disk']['source'].get('resource_group') is not None and \
|
||||
- kwargs[key]['os_disk']['source'].get('name') is not None:
|
||||
+ kwargs[key]['os_disk']['source'].get('name') is not None:
|
||||
resource_group = kwargs[key]['os_disk']['source'].get('resource_group')
|
||||
self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = ('/subscriptions/' +
|
||||
self.subscription_id +
|
||||
@@ -596,15 +597,16 @@ def exec_module(self, **kwargs):
|
||||
'/providers/Microsoft.Compute/snapshots/' +
|
||||
kwargs[key]['os_disk']['source'].get('name'))
|
||||
elif kwargs[key]['os_disk']['source'].get('uri') is not None and \
|
||||
- kwargs[key]['os_disk']['source'].get('resource_group') is not None and \
|
||||
- kwargs[key]['os_disk']['source'].get('storage_account') is not None:
|
||||
+ kwargs[key]['os_disk']['source'].get('resource_group') is not None and \
|
||||
+ kwargs[key]['os_disk']['source'].get('storage_account') is not None:
|
||||
resource_group = kwargs[key]['os_disk']['source'].get('resource_group')
|
||||
storage_account = kwargs[key]['os_disk']['source'].get('storage_account')
|
||||
self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = ('/subscriptions/' +
|
||||
self.subscription_id +
|
||||
'/resourceGroups/' +
|
||||
resource_group +
|
||||
- '/providers/Microsoft.Storage/storageAccounts/' +
|
||||
+ '/providers/Microsoft.Storage' +
|
||||
+ '/storageAccounts/' +
|
||||
storage_account)
|
||||
self.body['properties']['storageProfile']['osDiskImage']['source']['uri'] = kwargs[key]['os_disk']['source'].get('uri')
|
||||
else:
|
||||
|
||||
From 69afaab20550b0659dd6cad58edadbb8c081e8bd Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Cline <jeremycline@microsoft.com>
|
||||
Date: Fri, 1 Mar 2024 09:16:07 -0500
|
||||
Subject: [PATCH 3/3] Fix example and tests based on review comments
|
||||
|
||||
Add the missing "source" intermediate key in the example and test case,
|
||||
and ensure the VM information isn't shadowed by later steps.
|
||||
---
|
||||
plugins/modules/azure_rm_galleryimageversion.py | 7 ++++---
|
||||
.../targets/azure_rm_gallery/tasks/main.yml | 13 +++++++------
|
||||
2 files changed, 11 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/plugins/modules/azure_rm_galleryimageversion.py b/plugins/modules/azure_rm_galleryimageversion.py
|
||||
index cebad20b3..4befec9c3 100644
|
||||
--- a/plugins/modules/azure_rm_galleryimageversion.py
|
||||
+++ b/plugins/modules/azure_rm_galleryimageversion.py
|
||||
@@ -326,9 +326,10 @@
|
||||
storage_account_type: Standard_LRS
|
||||
storage_profile:
|
||||
os_disk:
|
||||
- resource_group: myResourceGroup
|
||||
- storage_account: myStorageAccount
|
||||
- uri: "https://myStorageAccount.blob.core.windows.net/myContainer/myImage.vhd"
|
||||
+ source:
|
||||
+ resource_group: myResourceGroup
|
||||
+ storage_account: myStorageAccount
|
||||
+ uri: "https://myStorageAccount.blob.core.windows.net/myContainer/myImage.vhd"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
diff --git a/tests/integration/targets/azure_rm_gallery/tasks/main.yml b/tests/integration/targets/azure_rm_gallery/tasks/main.yml
|
||||
index 0ebbe439f..ee1cbd9ed 100644
|
||||
--- a/tests/integration/targets/azure_rm_gallery/tasks/main.yml
|
||||
+++ b/tests/integration/targets/azure_rm_gallery/tasks/main.yml
|
||||
@@ -109,7 +109,7 @@
|
||||
azure_rm_virtualmachine_info:
|
||||
resource_group: "{{ resource_group }}"
|
||||
name: "vmforimage{{ rpfx }}"
|
||||
- register: output
|
||||
+ register: vm_output
|
||||
|
||||
- name: Create a snapshot by importing an unmanaged blob from the same subscription.
|
||||
azure_rm_snapshot:
|
||||
@@ -118,7 +118,7 @@
|
||||
location: eastus
|
||||
creation_data:
|
||||
create_option: Import
|
||||
- source_uri: 'https://{{ output.vms[0].storage_account_name }}.blob.core.windows.net/{{ output.vms[0].storage_container_name }}/{{ output.vms[0].storage_blob_name }}'
|
||||
+ source_uri: 'https://{{ vm_output.vms[0].storage_account_name }}.blob.core.windows.net/{{ vm_output.vms[0].storage_container_name }}/{{ vm_output.vms[0].storage_blob_name }}'
|
||||
register: output
|
||||
|
||||
- name: Assert the snapshot created
|
||||
@@ -372,10 +372,11 @@
|
||||
disk_encryption_set_id: "{{ des_results.state.id }}"
|
||||
storage_account_type: Standard_ZRS
|
||||
storage_profile:
|
||||
- source_image:
|
||||
- resource_group: "{{ resource_group }}"
|
||||
- storage_account: '{{ output.vms[0].storage_account_name }}'
|
||||
- uri: 'https://{{ output.vms[0].storage_account_name }}.blob.core.windows.net/{{ output.vms[0].storage_container_name }}/{{ output.vms[0].storage_blob_name }}'
|
||||
+ os_disk:
|
||||
+ source:
|
||||
+ resource_group: "{{ resource_group }}"
|
||||
+ storage_account: '{{ vm_output.vms[0].storage_account_name }}'
|
||||
+ uri: 'https://{{ vm_output.vms[0].storage_account_name }}.blob.core.windows.net/{{ vm_output.vms[0].storage_container_name }}/{{ vm_output.vms[0].storage_blob_name }}'
|
||||
register: output
|
||||
|
||||
- name: Assert the gallery image version created
|
||||
60
patches/azure-pr-1493.patch
Normal file
60
patches/azure-pr-1493.patch
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
From 9b4b5a5148d098d94e2307ad8ccc9392aae45463 Mon Sep 17 00:00:00 2001
|
||||
From: Jeremy Cline <jeremycline@microsoft.com>
|
||||
Date: Thu, 14 Mar 2024 09:51:43 -0400
|
||||
Subject: [PATCH] galleryimage: Support gallery image definitions for Arm64
|
||||
|
||||
Allow creating VM image definitions for Arm64 images.
|
||||
---
|
||||
plugins/modules/azure_rm_galleryimage.py | 18 ++++++++++++++++++
|
||||
1 file changed, 18 insertions(+)
|
||||
|
||||
diff --git a/plugins/modules/azure_rm_galleryimage.py b/plugins/modules/azure_rm_galleryimage.py
|
||||
index ce027c0c4..9a4542458 100644
|
||||
--- a/plugins/modules/azure_rm_galleryimage.py
|
||||
+++ b/plugins/modules/azure_rm_galleryimage.py
|
||||
@@ -76,6 +76,14 @@
|
||||
- V1
|
||||
- V2
|
||||
type: str
|
||||
+ architecture:
|
||||
+ description:
|
||||
+ - This property allows you to specify the hardware architecture of the Virtual Machines.
|
||||
+ - Arm64 is only supported with Hyper V Version 2.
|
||||
+ choices:
|
||||
+ - Arm64
|
||||
+ - x64
|
||||
+ type: str
|
||||
end_of_life_date:
|
||||
description:
|
||||
- The end of life date of the gallery Image Definition.
|
||||
@@ -275,6 +283,11 @@ def __init__(self):
|
||||
choices=['V1',
|
||||
'V2']
|
||||
),
|
||||
+ architecture=dict(
|
||||
+ type='str',
|
||||
+ choices=['Arm64',
|
||||
+ 'x64']
|
||||
+ ),
|
||||
end_of_life_date=dict(
|
||||
type='str',
|
||||
),
|
||||
@@ -408,6 +421,8 @@ def exec_module(self, **kwargs):
|
||||
self.body['properties']['osState'] = kwargs[key]
|
||||
elif key == 'hypervgeneration':
|
||||
self.body['properties']['hyperVGeneration'] = kwargs[key]
|
||||
+ elif key == 'architecture':
|
||||
+ self.body['properties']['architecture'] = kwargs[key]
|
||||
elif key == 'end_of_life_date':
|
||||
self.body['properties']['endOfLifeDate'] = kwargs[key]
|
||||
elif key == 'identifier':
|
||||
@@ -490,6 +505,9 @@ def exec_module(self, **kwargs):
|
||||
elif (self.body['properties'].get('hyperVGeneration') is not None and
|
||||
self.body['properties']['hyperVGeneration'] != old_response['properties'].get('hyperVGeneration')):
|
||||
self.to_do = Actions.Update
|
||||
+ elif (self.body['properties'].get('architecture') is not None and
|
||||
+ self.body['properties']['architecture'] != old_response['properties'].get('architecture')):
|
||||
+ self.to_do = Actions.Update
|
||||
elif (self.body['properties'].get('endOfLifeDate') is not None and
|
||||
self.body['properties']['endOfLifeDate'] != old_response['properties'].get('endOfLifeDate')):
|
||||
self.to_do = Actions.Update
|
||||
27
pyproject.toml
Normal file
27
pyproject.toml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "fedora-cloud-image-uploader"
|
||||
description = "An AMQP message consumer that uploads Fedora images to clouds."
|
||||
readme = "README.md"
|
||||
license = {file = "LICENSE"}
|
||||
dynamic = ["version"]
|
||||
requires-python = ">=3.10"
|
||||
|
||||
dependencies = [
|
||||
"ansible",
|
||||
"ansible-runner",
|
||||
"click",
|
||||
"fedora-messaging",
|
||||
"koji-fedoramessaging-messages",
|
||||
"requests",
|
||||
"setuptools",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
fedora-cloud-image-uploader = "fedora_cloud_image_uploader.cli:main"
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "fedora_cloud_image_uploader/__init__.py"
|
||||
9271
test/sample_message.json
Normal file
9271
test/sample_message.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue