-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
testing container in OpenShift #164
Conversation
# INTERFACE CLASSES FOR SPECIFIC MODULE TESTS | ||
class OpenShiftAvocadoTest(AvocadoTest): | ||
""" | ||
Class for writing tests specific just for DOCKER |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/DOCKER/OpenShift/
self.cancel("OpenShift specific test") | ||
super(OpenShiftAvocadoTest, self).setUp() | ||
|
||
def checkLabel(self, key, value): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this method up to date?
|
||
def stop(self): | ||
""" | ||
Stop the docker container |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docstring not up to date
""" | ||
start the OpenShift application | ||
|
||
:param args: Do not use it directly (It is defined in config.yaml) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method doesn't accept any arguments
self.app_ip = service["items"][0]["spec"]["clusterIP"] | ||
common.trans_dict['GUESTIPADDR'] = self.app_ip | ||
common.print_info(common.trans_dict) | ||
return True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docstring says that this method should return an ip address
common.trans_dict['GUESTIPADDR'] = self.app_ip | ||
common.print_info(common.trans_dict) | ||
return True | ||
except KeyError as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can also throw IndexError and TypeError (None[0]
)
service = self._convert_string_to_json(oc_get_service.stdout) | ||
try: | ||
common.print_info(service["items"]) | ||
common.print_info(service["items"][0]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what if there are other services running?
""" | ||
return json.loads( | ||
self.runHost( | ||
"docker inspect %s" % |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
openshift is not tied to docker
|
||
def _openshift_login(self, oc_ip="127.0.0.1", oc_user='developer', oc_passwd='developer', env=False): | ||
if env: | ||
if 'OPENSHIFT_IP' in os.environ: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
documentation would be lovely
Signed-off-by: Petr "Stone" Hracek <[email protected]>
Signed-off-by: Petr "Stone" Hracek <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
docs still need more love
**OpenShift** | ||
|
||
- Install OpenShift if not installed and if environment variable ``OPENSHIFT_LOCAL`` is specified. | ||
- if ``OPENSHIFT_LOCAL`` variable is specified, the it starts an OpenShift by command ``oc cluster up`` or stops it by command ``oc cluster down``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then*
- **OPENSHIFT_LOCAL** enables installing ``origin`` and ``origin-clients`` on local machine | ||
- **OPENSHIFT_IP** uses this IP address for connecting to an OpenShift environment. | ||
- **OPENSHIFT_USER** uses this ``USER`` name for login to an OpenShift environment. | ||
- **OPENSHIFT_PWD** uses this ``PWD`` name for login to an OpenShift environment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PWD in our world means "print working directory"
@@ -25,6 +25,10 @@ Environment variables allow to overwrite some values of a module configuration f | |||
- **MTF_REMOTE_REPOS=yes** disables downloading of Koji packages and creating a local repo, and speeds up test execution. | |||
- **MTF_DISABLE_MODULE=yes** disables module handling to use nonmodular test mode (see `multihost tests`_ as an example). | |||
- **DOCKERFILE="<path_to_dockerfile"** overwrites the location of a Dockerfile. | |||
- **OPENSHIFT_LOCAL** enables installing ``origin`` and ``origin-clients`` on local machine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be nice to follow the previous definitions: e.g. what is the sample value for this variable?
from moduleframework.mtfexceptions import ContainerExc, ConfigExc | ||
|
||
|
||
class OpenShiftHelper(CommonFunctions): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So how do I actually use this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is used from commandline sudo MODULE=openshift mtf
which executes this class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sweet! include it in docs please
Signed-off-by: Petr "Stone" Hracek <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for this PR, few comments inside code
moduleframework/common.py
Outdated
@@ -513,6 +513,8 @@ def getIPaddr(self): | |||
|
|||
:return: str | |||
""" | |||
if 'openshift' == os.environ.get('MODULE'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this? in case you set self.ipaddr
in Openshift helper it return proper value.
|
||
def __prepare_selinux(self): | ||
# disable selinux by default if not turned off | ||
if not os.environ.get('MTF_SKIP_DISABLING_SELINUX'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this? openshift
is also crappy with selinux
enabled (nspawn is crappy with selinux enabled)?
|
||
:return: None | ||
""" | ||
if os.environ.get('OPENSHIFT_LOCAL'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please move os.environ.get('OPENSHIFT_LOCAL')
to own function in common module and then use it instead of this env variable check.
from moduleframework.mtfexceptions import ContainerExc, ConfigExc | ||
|
||
|
||
class OpenShiftHelper(CommonFunctions): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doen't make bigger sense to have ContainerHelper
as parent instead of CommonFunctions
in case docker is close to openshitft. in case it does not make sense, then this is okay.
and also remember CONU project. If we expect similar code in CONU, woule be better to be closer to docker class, and then you can remove some functions what are already defined in ContainerHelper
self.app_ip = None | ||
common.print_debug(self.icontainer, self.app_name) | ||
|
||
def get_container_url(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as an example, this method is same as in ContainerHelper
""" | ||
if env: | ||
if 'OPENSHIFT_IP' in os.environ: | ||
oc_ip = os.environ.get('OPENSHIFT_IP') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please move all os.environ.get
to separated function, then you don't need to do this code and do it simplier and cleaner like: oc_ip = common.get_os_ip() or oc_ip
oc_user, | ||
oc_passwd), | ||
verbose=common.is_not_silent()) | ||
common.print_debug(oc_output.stdout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why you use this debugging? isn't better to use verbose=variable
directly. it exactly does what you want
verbose=is_debug()
print via avocado run command output to logger and it is there. in case DEBUG is not set, it is not printed out. (when you don't use it with avocado runner, log is not shown, but it can be enabled.)
try: | ||
for svc in service["items"]: | ||
if "clusterIP" in svc.get("spec"): | ||
common.trans_dict['GUESTIPADDR'] = svc.get("spec").get("clusterIP") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe this is proper place where to modify also self.ipaddr
variable and then you can remove some special handling of ipadd in common.CommonFunciton for OS
and maybe question, why there is for cycle. means that there can be more ipadresses and and the last one is the best one?
or you meant code like:
for svc un service.get("items", []):
ipaddr = svc.get("spec",{}).get("clusterIP")
if ipaddr:
common.trans_dict['GUESTIPADDR'] = ipaddr
self.ipaddr = ipaddr
return True
return False
?
:param kwargs: dict | ||
:return: avocado.process.run | ||
""" | ||
return self.runHost("%s" % common.sanitize_cmd(command), **kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that this does what you expect?
this run command on host.
run
method of helper should do command on GUEST in this case on openshift instance in case of docker there is for example docker exec INSTANCE_ID command
so in case of openshift I expect something similar like oc exec ...
it is connected with more missing special methods for openshift, like CopyFrom
and CopyTo
now are inherited from CommonFunctions, what leads to host to host
implementation, I expect there also some special oc
command, or do it via some ssh, or whatever how it is connected together
Probably my mistake were that I've defined these functions leads to localhost
operations, instead of raising not defined exceptions, and implement these methods where it make sense like in RpmHelper, ContainerHelper
etc...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The functionality seems like exactly what we need -- basic testing using oc new-app
. The one comment I have here is that users should probably be able to construct the CLI themselves, the same as you do for docker run
.
For implementation: it's really fragile -- sometimes you get json from the commands (good) and process it, sometimes you don't. You also ignore the fact that they may be other applications being deployed in the cluster -- or even other MTF tests being run in parallel.
False, application does not exist | ||
""" | ||
oc_status = self.runHost("oc status", ignore_status=True) | ||
if 'dc/%s' % self.app_name in oc_status.stdout: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not doing oc get dc $APP_NAME
?
return True | ||
oc_services = self.runHost("oc get services -o json") | ||
json_svc = self._convert_string_to_json(oc_services.stdout) | ||
if not json_svc["items"]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so if I have ANY service deployed in my openshift cluster, this will return true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as @TomasTomecek mentioned, probably you need to add there some "service" specific calls. I don't know it but probably there will be something similar to docker container inspect CONTAINER_ID
what will show state just for one service instead of list all services and try to get some data
# dovecot 172.30.1.1:5000/myproject/dovecot latest 15 minutes ago | ||
# memcached 172.30.1.1:5000/myproject/memcached latest 13 minutes ago | ||
|
||
app_found = [x for x in oc_get.split('\n') if x.startswith(self.app_name)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is really fragile, why not using json as an output from oc get
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will try to use it.
It creates an application in OpenShift environment | ||
""" | ||
# Switching to system user | ||
oc_new_app = self.runHost("oc new-app %s --name=%s" % (self.container_name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should add a label here to mark the deployment being owned by mtf: oc new-app -l mtf_testing=true
or something like that; and then operate on objects which have this label.
Edit: so we probably need two identifiers: one which mark the app as one deployed by MTF, and the other will be unique identifier for the test run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@phracek @TomasTomecek yep, similar to docker container ...
commands and then you will just use this tag. otherwise I expect that this iddentifier will be stored in OpenShiftHelper class like self.jmeno
for ContainerHelper
""" | ||
pod_initiated = False | ||
for x in range(0, 20): | ||
pod_state = self.runHost("oc get pods", ignore_status=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use json and don't parse CLI output
:param oc_user: an username under which we can login to OpenShift environment | ||
:param oc_passwd: a password for specific username | ||
:param env: | ||
:return: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment should be: env: is used for specification OpenShift IP, user and password, otherwise defaults are used
:param env: | ||
:return: | ||
""" | ||
if env: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably don't understand this logic
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this make sense to you?
env: is used for specification OpenShift IP, user and password, otherwise defaults are used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, I still don't understand the reason for that variablee, since the values are being picked up from environment
""" | ||
super(OpenShiftHelper, self).tearDown() | ||
if common.get_if_do_cleanup(): | ||
# TODO will be implemented later on. I have to find a usecase what to remove |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably remove everything which was put into openshift during this test
|
||
def status(self): | ||
""" | ||
get status of an OpenShift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't seem to correspond with the actual code
|
||
:return: bool | ||
""" | ||
if self._app_exists(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you can easily return self._app_exists()
Signed-off-by: Petr "Stone" Hracek <[email protected]>
Signed-off-by: Petr "Stone" Hracek <[email protected]>
Signed-off-by: Petr "Stone" Hracek <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks a lot better! Can't wait to give this a shot!
I think that some docstrings are still misleading and would love to have them improved.
|
||
def status(self): | ||
""" | ||
get status of an application in OpenShift environment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think that this docstring doesn't correspond with the code: what does the "status" mean exactly? For me, status is usually enum, something like "running", "failed", "finished" and this method returns bool. It makes me confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be solved in whole MTF. status
function always return bool
value like e.g. here: https://github.com/fedora-modularity/meta-test-family/blob/devel/moduleframework/common.py#L552
https://github.com/fedora-modularity/meta-test-family/blob/devel/moduleframework/helpers/container_helper.py#L171
I have filed an issue for it here: #168
I will keep it now as it is.
|
||
def stop(self): | ||
""" | ||
Stops the OpenShift |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
another misleading docstring; I think this method actually removes the application from the cluster
""" | ||
starts the OpenShift application | ||
|
||
:param args: Do not use it directly (It is defined in config.yaml) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function doesn't accept any args
common.print_info(e.message) | ||
return False | ||
|
||
def getIPaddr(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pythonic way of doing setters and getters is like this: https://docs.python.org/3/library/functions.html#property
(not blocking this review, just a friendly advice)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have filed an issue for it #167
service = self._convert_string_to_json(oc_get_service.stdout) | ||
try: | ||
for svc in service: | ||
if svc.get('metadata').get('labels').get('app') == self.app_name: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
|
||
def _get_ip_instance(self): | ||
""" | ||
It gets and IP address of an application from of OpenShift POD. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about: "This method verifies that we can obtain an IP address of the application deployed within OpenShift."
Hopefully, I have addressed all issues. The rest are filed as a new issues. |
Signed-off-by: Petr "Stone" Hracek <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, nicely done! let's give this a go
This Pull Request brings possibility to test containers in OpenShift environment.
MTF provides two scripts for preparing environment like
MODULE=openshift mtf-env-set
andMODULE=openshift mtf-env-clean
Usage is like:
MODULE=openshift OPENSHIFT_IP="Openshift_IP_Address" OPENSHIFT_USERNAME=developer OPENSHIFT_PASSWD=developer mtf -l *.py
The Pull Request fixes issue #89