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
Unit test install strategy ALM-227 #132
Unit test install strategy ALM-227 #132
Conversation
ecordell
commented
Oct 30, 2017
- refactored install strategy bit to separate concerns and make things more testable
- added a client interface that the install strategy uses
- added tests for install behavior for when things exist in the cluster and when they don't
c856695
to
c24854b
Compare
ed34e85
to
eb2937f
Compare
@@ -130,6 +130,7 @@ const ( | |||
CSVReasonRequirementsNotMet ConditionReason = "RequirementsNotMet" | |||
CSVReasonRequirementsMet ConditionReason = "AllRequirementsMet" | |||
CSVReasonComponentFailed ConditionReason = "InstallComponentFailed" | |||
CSVReasonInvalidStrategy ConditionReason = "InstallStrategyInvalid" |
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.
InstallStraegyInvalid
reads a little weird to me. Can we reverse the order on it, i.e. InvalidInstallStrategy
?
client/deployment_install_client.go
Outdated
type InstallStrategyDeploymentInterface interface { | ||
CreateRole(role *v1beta1rbac.Role) (*v1beta1rbac.Role, error) | ||
CreateRoleBinding(roleBinding *v1beta1rbac.RoleBinding) (*v1beta1rbac.RoleBinding, error) | ||
GetOrCreateServiceAccount(serviceAccount *v1.ServiceAccount) (*v1.ServiceAccount, error) |
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 expected behavior of this function isn't very clear from the name. I think simply making it CreateServiceAccount
where it's a no-op if it already exists (or EnsureServiceCountExists
?) is fine.
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 actually does return the ServiceAccount it finds if it's already there - it's not a no-op if it already 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.
It didn't look like anything other than the name, which is set in alm/install/deployment and by necessity the same.
Should this really be an upsert, i.e. CreateOrUpdateServiceAccount
? I think what's throwing me off is maybe the service account will match what you gave the function, maybe it won't (if it already exists).
client/deployment_install_client.go
Outdated
CreateRoleBinding(roleBinding *v1beta1rbac.RoleBinding) (*v1beta1rbac.RoleBinding, error) | ||
GetOrCreateServiceAccount(serviceAccount *v1.ServiceAccount) (*v1.ServiceAccount, error) | ||
CreateDeployment(deployment *v1beta1extensions.Deployment) (*v1beta1extensions.Deployment, error) | ||
CheckServiceAccount(serviceAccountName string) (bool, error) |
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.
Check what? It might be simpler to just have this be the simple GetServiceAccountByName
, otherwise maybe CheckServiceAccountExists
or something else to clarify what this means.
client/deployment_install_client.go
Outdated
GetOrCreateServiceAccount(serviceAccount *v1.ServiceAccount) (*v1.ServiceAccount, error) | ||
CreateDeployment(deployment *v1beta1extensions.Deployment) (*v1beta1extensions.Deployment, error) | ||
CheckServiceAccount(serviceAccountName string) (bool, error) | ||
CheckOwnedDeployments(owner metav1.ObjectMeta, deploymentSpecs []v1beta1extensions.DeploymentSpec) (bool, error) |
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.
Same here. This function weirdly mixes client and consumer concerns. Doing GetOwnedDeployments
and having the compare logic in install/deployment.go makes it much easier to understand what's going on.
client/deployment_install_client.go
Outdated
|
||
type InstallStrategyDeploymentClient struct { | ||
opClient opClient.Interface | ||
Namespace string |
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'm unclear on how this namespace is used. Are all actions scoped to this namespace? As a consumer of this client, it's very opaque. Either consistently use Namespace
and rename this like InstallStrategyDeploymentClientForNamespace
or whatever, or (my preference) add namespace string
as an argument to each function in the interface that needs it.
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.
Yeah, all actions are restricted to the namespace. I can make that clear with the name
client/deployment_install_client.go
Outdated
func (c *InstallStrategyDeploymentClient) CheckOwnedDeployments(owner metav1.ObjectMeta, deploymentSpecs []v1beta1extensions.DeploymentSpec) (bool, error) { | ||
existingDeployments, err := c.opClient.ListDeploymentsWithLabels(c.Namespace, map[string]string{ | ||
"alm-owner-name": owner.Name, | ||
"alm-owner-namespace": owner.Namespace, |
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.
Personal pref, I would prefer to have CheckOwnedDeployments(ownerName, ownerNamespace...)
or whatever to clarify what it's matching on.
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.
We might change this query to user ownerreferences in the future, so I think it makes sense to keep the whole owner
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 I have some fear still from experiences where parts of the codebase break unexpectedly when you change something about the object because it relied on some obscure field on the object you didn't even know about.
I'll trust your judgement on where we need flexibility more than guaranteeing future stability. 👍
install/deployment.go
Outdated
|
||
func NewStrategyDeploymentInstaller(opClient opClient.Interface, ownerMeta metav1.ObjectMeta, ownerType metav1.TypeMeta) StrategyInstaller { | ||
return &StrategyDeploymentInstaller{ | ||
strategyClient: client.NewInstallStrategyDeploymentClient(opClient, ownerMeta.Namespace), |
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's a little awkward, but better separation of concerns if the consumer instantiates and passes along the alm/client instead of opClient; means we can remove operator-client from the entire alm/install
package and cleanly pass the client from the ALM operator's instantiation (create the alm/client somewhere after here: https://github.com/ecordell/alm/blob/eb2937fb06723562be5d077b860b70a8368ce367/queueinformer/queueinformer_operator.go#L22)
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.
Moved it up a level but it's hard to move it much higher than the strategy resolver because each install strategy will need a different client:
func (r *StrategyResolver) InstallerForStrategy(strategyName string, opClient opClient.Interface, ownerMeta metav1.ObjectMeta) StrategyInstaller {
switch strategyName {
case InstallStrategyNameDeployment:
strategyClient := client.NewInstallStrategyDeploymentClient(opClient, ownerMeta.Namespace)
return NewStrategyDeploymentInstaller(strategyClient, ownerMeta)
}
//...
}
When we get to the point of having more than one install strategy type we can revisit refactoring
install/deployment.go
Outdated
type StrategyDeploymentInstaller struct { | ||
strategyClient client.InstallStrategyDeploymentInterface | ||
ownerMeta metav1.ObjectMeta | ||
ownerType metav1.TypeMeta |
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 know this isn't from commits in this pr but it looks like ownerType
is never used.
7817dc8
to
2bc5973
Compare
a7ae91a
to
86117dd
Compare
client/deployment_install_client.go
Outdated
} | ||
if err == nil { | ||
return foundAccount, nil | ||
} |
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 might be clearer to do,
if err == nil {
return foundAccount, nil
}
if !apierrors.IsNotFound(err) {
return nil, errors.Wrap(err, "checking for existing serviceacccount failed")
}
client/deployment_install_client.go
Outdated
if err != nil && !apierrors.IsAlreadyExists(err) { | ||
return createdAccount, errors.Wrap(err, "creating serviceacccount failed") | ||
} | ||
return createdAccount, nil |
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 you need to re-fetch the serviceaccount if you get a IsAlreadyExists
error?
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.
Nope, we only need the name to create the rolebinding
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.
But won't createdAccount
be nil? You could return serviceAccount
but I still find it unclear about what you're getting back. In the first Get
, the return value foundAccount
!= serviceAccount
necessarily. And if the Create
is successful, the createdAccount
will have additional data on it that serviceAccount
does not. I'd rather have consistent behavior, which might mean updating the service account if found.
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 will be nil, but that's the correct value for it for this return. I've changed it explicitly to nil so that it's clear.
install/deployment.go
Outdated
|
||
// Check deployments | ||
if found, err := i.checkForOwnedDeployments(i.ownerMeta, strategy.DeploymentSpecs); !found { | ||
log.Infof("deployments not found") |
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.
log.Errorf
?
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.
Changed to debugf, which fits a bit better I think
4ecd4c7
to
1bbd0ec
Compare
client/deployment_install_client.go
Outdated
if err != nil && !apierrors.IsAlreadyExists(err) { | ||
return createdAccount, errors.Wrap(err, "creating serviceacccount failed") | ||
} | ||
return createdAccount, nil |
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.
But won't createdAccount
be nil? You could return serviceAccount
but I still find it unclear about what you're getting back. In the first Get
, the return value foundAccount
!= serviceAccount
necessarily. And if the Create
is successful, the createdAccount
will have additional data on it that serviceAccount
does not. I'd rather have consistent behavior, which might mean updating the service account if found.
1bbd0ec
to
5fc7538
Compare
5fc7538
to
65a0c84
Compare
…tall-strategy Unit test install strategy ALM-227
…tall-strategy Unit test install strategy ALM-227