-
Notifications
You must be signed in to change notification settings - Fork 117
Enable testing against GCE clusters #243
Conversation
|
I'd like to get this one reviewed before all else, because it makes the workflow a bit simpler without minikube. |
| val SingleNode, MultiNode = Value | ||
| } | ||
|
|
||
| object KubernetesClient { |
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.
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.
Or perhaps renamed to KubernetesIntegrationTestsFixtures. Primarily it would be good to:
- Contrast this with
KubernetesTestComponentsif they are different, and - Rename from
KubernetesClientsince this is also used by the Fabric8 client - maybeKubernetesTestClient?
Some Scaladoc on this class and its purpose could be a good idea, e.g. "Controls the Kubernetes cluster that integration tests are run against."
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.
KubernetesTestClient makes sense. Will do & add doc.
|
rerun unit tests please |
| val SingleNode, MultiNode = Value | ||
| } | ||
|
|
||
| object KubernetesClient { |
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 don't think this necessarily has to be an object as a static singleton. Similarly for Minikube - we did start off with that being an object but it would probably be cleaner to use classes and dynamic instances if only to move away from global state.
If KubernetesV1Suite and KubernetesV2Suite need to share instances of these, they can be passed into the constructors from the parent KubernetesSuite#nestedSuites.
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.
Passing it around everywhere seemed less than ideal. Since this will be shared across all testsuites, I felt this was easier to understand. Is it an anti-pattern here?
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.
For Minikube considering it's managing global state anyways (the execution of the Minikube binary) that one perhaps makes sense to stay as an object. Otherwise, I think making the sharing explicit in the method contract would make the test dependencies easier to trace around. For example, right now KubernetesV2Suite can be executed on its own e.g. in IntelliJ but when it does it won't have the lifecycle management that KubernetesSuite is setting up.
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.
Hmm.. I see. That's a good point. It seems like if I put in those args, we'll lose the ability to run each of those tests separately from intellij. Any way to keep both? What do you think of having each testsuite (v1 and v2) initialize its own client? Since eventually we're only going to have one, it would be temporary.
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 this was the idea we had before - KubernetesTestComponents is responsible for the Kubernetes client, but the parent suite KubernetesSuite is responsible for the global state, that is, the Minikube cluster. Global state should still be kept in the Minikube object - the user can run each individual test by starting Minikube and building the docker images themselves manually, for example. We essentially want to preserve these semantics.
| } | ||
|
|
||
| private def createDefaultClient(): Unit = { | ||
| System.getProperty("spark.docker.test.master") match { |
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.
Option(System.getProperty(...)).map(...).getOrElse(...)
| import org.apache.spark.internal.Logging | ||
| import org.apache.spark.util.Utils | ||
|
|
||
| object Util extends Logging { |
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.
Use specific names for different kinds of utilities. E.g. UnixProcessUtils.
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.
Javadoc this method also.
|
|
||
| object KubernetesClient { | ||
| var defaultClient: DefaultKubernetesClient = _ | ||
| var testBackend: TestBackend.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.
This could perhaps be thought of in a polymorphic approach. What if we had IntegrationTestBackend as the parent class with methods like initialize (Minikube builds docker images), getKubernetesClient and cleanUp, then, a factory class switches on the setting of the system property to choose the implementation.
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.
Instead of getKubernetesClient being an abstract method on such an abstract parent class, maybe getKubernetesClient is a final method which invokes the polymorphic getKubernetesClientConfig which varies depending on the backend.
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.
That's a good idea. I'm tempted to punt it for later though, since right now there's still quite a bit of Minikube being used directly in a lot of places. Ideally, once we move all of it out and clearly define the boundaries of backends, we can abstract out all the cluster specific operations into IntegrationTestBackend abstract parent, or trait. WDYT?
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 believe that the polymorphic test architecture could belong in the scope of this PR - if we're moving towards multiple back ends, it would be nice if we captured the overall architecture on this pass. If we are concerned that such a change would create too large of a diff, can we create a separate PR that merges into this one that implements the polymorphic architecture, merge said separate PR first into this branch, and then merge this PR?
This is primarily to ease review. Much of what could be commented on for this PR could be contingent on whether or not a polymorphic approach is 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.
Dependent PR works. I'll send one out later today then.
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.
Opened #248
|
+1! |
* Restructured the test backends * Address comments * var -> val
| servicePortName: String, | ||
| servicePath: String = ""): T = synchronized { | ||
| val kubernetesMaster = s"${defaultClient.getMasterUrl}" | ||
| val httpClient = getHttpClient(kubernetesClient.asInstanceOf[BaseClient]) |
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.
Do we use this anywhere?
mccheah
left a comment
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.
Missed a tiny detail from the upstream PR. There also appears to be an unused http client field. After those changes I'm ok to merge this.
| private[spark] object IntegrationTestBackendFactory { | ||
| def getTestBackend(): IntegrationTestBackend = { | ||
| Option(System.getProperty("spark.kubernetes.test.master")).map { | ||
| master => |
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.
Nit - Can use .map(new GCETestBackend(_)) where the underscore passes in the master.
|
Looks like the integration test is wedged. @kimoonkim @varunkatta could you please restart it? |
|
@mccheah tests passed. ok to merge? |
| HttpClientUtil.createClient[T](Set(url), 5, sslContext.getSocketFactory, trustManager) | ||
| } | ||
|
|
||
| def getHttpClient(client: BaseClient): OkHttpClient = { |
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.
Do we use this anywhere?
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 like I missed that one. Sorry!
We'll likely need that http client later, because I think we shouldn't rely on minikube credentials to hit the api-server proxy endpoint, but I'll get rid of it now.
* Part 1: making test code cluster-agnostic * Final checked * Move all test code into KubernetesTestComponents * Addressed comments * Fixed doc * Restructure the test backends (#248) * Restructured the test backends * Address comments * var -> val * Comments * removed deadcode
* Part 1: making test code cluster-agnostic * Final checked * Move all test code into KubernetesTestComponents * Addressed comments * Fixed doc * Restructure the test backends (apache-spark-on-k8s#248) * Restructured the test backends * Address comments * var -> val * Comments * removed deadcode
Changes to the test framework to run a subset of the tests we have now against a GCE cluster
I'll convert the other tests to work against multi-node clusters in subsequent PRs.
cc @mccheah @ash211