Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions apis/mssql/v1alpha1/user_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,26 @@ type UserStatus struct {

// UserParameters define the desired state of a MSSQL user instance.
type UserParameters struct {
// Database allows you to specify the name of the Database the USER is created for.
// +crossplane:generate:reference:type=Database
Database *string `json:"database,omitempty"`
// DatabaseRef allows you to specify custom resource name of the Database
// DatabaseRef allows you to specify custom resource name of the Database the USER is created for.
// to fill Database field.
DatabaseRef *xpv1.Reference `json:"databaseRef,omitempty"`
// DatabaseSelector allows you to use selector constraints to select a
// Database.
// DatabaseSelector allows you to use selector constraints to select a Database the USER is created for.
DatabaseSelector *xpv1.Selector `json:"databaseSelector,omitempty"`
// PasswordSecretRef references the secret that contains the password used
// for this user. If no reference is given, a password will be auto-generated.
// +optional
PasswordSecretRef *xpv1.SecretKeySelector `json:"passwordSecretRef,omitempty"`
// LoginDatabase allows you to specify the name of the Database to be used to create the user LOGIN in (normally master).
// +crossplane:generate:reference:type=Database
LoginDatabase *string `json:"loginDatabase,omitempty"`
// DatabaseRef allows you to specify custom resource name of the Database to be used to create the user LOGIN in (normally master).
// to fill Database field.
LoginDatabaseRef *xpv1.Reference `json:"loginDatabaseRef,omitempty"`
// DatabaseSelector allows you to use selector constraints to select a Database to be used to create the user LOGIN in (normally master).
LoginDatabaseSelector *xpv1.Selector `json:"loginDatabaseSelector,omitempty"`
}

// A UserObservation represents the observed state of a MSSQL user.
Expand Down
15 changes: 15 additions & 0 deletions apis/mssql/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions apis/mssql/v1alpha1/zz_generated.resolvers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 86 additions & 3 deletions package/crds/mssql.sql.crossplane.io_users.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ spec:
instance.
properties:
database:
description: Database allows you to specify the name of the Database
the USER is created for.
type: string
databaseRef:
description: |-
DatabaseRef allows you to specify custom resource name of the Database
DatabaseRef allows you to specify custom resource name of the Database the USER is created for.
to fill Database field.
properties:
name:
Expand Down Expand Up @@ -110,9 +112,90 @@ spec:
- name
type: object
databaseSelector:
description: DatabaseSelector allows you to use selector constraints
to select a Database the USER is created for.
properties:
matchControllerRef:
description: |-
MatchControllerRef ensures an object with the same controller reference
as the selecting object is selected.
type: boolean
matchLabels:
additionalProperties:
type: string
description: MatchLabels ensures an object with matching labels
is selected.
type: object
policy:
description: Policies for selection.
properties:
resolution:
default: Required
description: |-
Resolution specifies whether resolution of this reference is required.
The default is 'Required', which means the reconcile will fail if the
reference cannot be resolved. 'Optional' means this reference will be
a no-op if it cannot be resolved.
enum:
- Required
- Optional
type: string
resolve:
description: |-
Resolve specifies when this reference should be resolved. The default
is 'IfNotPresent', which will attempt to resolve the reference only when
the corresponding field is not present. Use 'Always' to resolve the
reference on every reconcile.
enum:
- Always
- IfNotPresent
type: string
type: object
type: object
loginDatabase:
description: LoginDatabase allows you to specify the name of the
Database to be used to create the user LOGIN in (normally master).
type: string
loginDatabaseRef:
description: |-
DatabaseSelector allows you to use selector constraints to select a
Database.
DatabaseRef allows you to specify custom resource name of the Database to be used to create the user LOGIN in (normally master).
to fill Database field.
properties:
name:
description: Name of the referenced object.
type: string
policy:
description: Policies for referencing.
properties:
resolution:
default: Required
description: |-
Resolution specifies whether resolution of this reference is required.
The default is 'Required', which means the reconcile will fail if the
reference cannot be resolved. 'Optional' means this reference will be
a no-op if it cannot be resolved.
enum:
- Required
- Optional
type: string
resolve:
description: |-
Resolve specifies when this reference should be resolved. The default
is 'IfNotPresent', which will attempt to resolve the reference only when
the corresponding field is not present. Use 'Always' to resolve the
reference on every reconcile.
enum:
- Always
- IfNotPresent
type: string
type: object
required:
- name
type: object
loginDatabaseSelector:
description: DatabaseSelector allows you to use selector constraints
to select a Database to be used to create the user LOGIN in
(normally master).
properties:
matchControllerRef:
description: |-
Expand Down
36 changes: 22 additions & 14 deletions pkg/controller/mssql/user/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,23 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E
return nil, errors.Wrap(err, errGetSecret)
}

userDB := c.newClient(s.Data, ptr.Deref(cr.Spec.ForProvider.Database, ""))
loginDB := userDB
if cr.Spec.ForProvider.LoginDatabase != nil {
loginDB = c.newClient(s.Data, ptr.Deref(cr.Spec.ForProvider.LoginDatabase, ""))
}

return &external{
db: c.newClient(s.Data, ptr.Deref(cr.Spec.ForProvider.Database, "")),
kube: c.kube,
userDB: userDB,
loginDB: loginDB,
kube: c.kube,
}, nil
}

type external struct {
db xsql.DB
kube client.Client
userDB xsql.DB
loginDB xsql.DB
kube client.Client
}

func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) {
Expand All @@ -139,7 +147,7 @@ func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
var name string

query := "SELECT name FROM sys.database_principals WHERE type = 'S' AND name = @p1"
err := c.db.Scan(ctx, xsql.Query{
err := c.userDB.Scan(ctx, xsql.Query{
String: query, Parameters: []interface{}{
meta.GetExternalName(cr),
},
Expand Down Expand Up @@ -182,21 +190,21 @@ func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.Ext
}

loginQuery := fmt.Sprintf("CREATE LOGIN %s WITH PASSWORD=%s", mssql.QuoteIdentifier(meta.GetExternalName(cr)), mssql.QuoteValue(pw))
if err := c.db.Exec(ctx, xsql.Query{
if err := c.loginDB.Exec(ctx, xsql.Query{
String: loginQuery,
}); err != nil {
return managed.ExternalCreation{}, errors.Wrapf(err, errCreateLogin, meta.GetExternalName(cr))
}

userQuery := fmt.Sprintf("CREATE USER %s FOR LOGIN %s", mssql.QuoteIdentifier(meta.GetExternalName(cr)), mssql.QuoteIdentifier(meta.GetExternalName(cr)))
if err := c.db.Exec(ctx, xsql.Query{
if err := c.userDB.Exec(ctx, xsql.Query{
String: userQuery,
}); err != nil {
return managed.ExternalCreation{}, errors.Wrapf(err, errCreateUser, meta.GetExternalName(cr))
}

return managed.ExternalCreation{
ConnectionDetails: c.db.GetConnectionDetails(meta.GetExternalName(cr), pw),
ConnectionDetails: c.userDB.GetConnectionDetails(meta.GetExternalName(cr), pw),
}, nil
}

Expand All @@ -213,14 +221,14 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext

if changed {
query := fmt.Sprintf("ALTER LOGIN %s WITH PASSWORD=%s", mssql.QuoteIdentifier(meta.GetExternalName(cr)), mssql.QuoteValue(pw))
if err := c.db.Exec(ctx, xsql.Query{
if err := c.loginDB.Exec(ctx, xsql.Query{
String: query,
}); err != nil {
return managed.ExternalUpdate{}, errors.Wrap(err, errUpdateUser)
}

return managed.ExternalUpdate{
ConnectionDetails: c.db.GetConnectionDetails(meta.GetExternalName(cr), pw),
ConnectionDetails: c.userDB.GetConnectionDetails(meta.GetExternalName(cr), pw),
}, nil
}
return managed.ExternalUpdate{}, nil
Expand All @@ -233,7 +241,7 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) error {
}

query := fmt.Sprintf("SELECT session_id FROM sys.dm_exec_sessions WHERE login_name = %s", mssql.QuoteValue(meta.GetExternalName(cr)))
rows, err := c.db.Query(ctx, xsql.Query{String: query})
rows, err := c.userDB.Query(ctx, xsql.Query{String: query})
if err != nil {
return errors.Wrap(err, errCannotGetLogins)
}
Expand All @@ -244,21 +252,21 @@ func (c *external) Delete(ctx context.Context, mg resource.Managed) error {
if err := rows.Scan(&sessionID); err != nil {
return errors.Wrap(err, errCannotGetLogins)
}
if err := c.db.Exec(ctx, xsql.Query{String: fmt.Sprintf("KILL %d", sessionID)}); err != nil {
if err := c.userDB.Exec(ctx, xsql.Query{String: fmt.Sprintf("KILL %d", sessionID)}); err != nil {
return errors.Wrapf(err, errCannotKillLoginSession, sessionID, meta.GetExternalName(cr))
}
}
if err := rows.Err(); err != nil {
return errors.Wrap(err, errCannotGetLogins)
}

if err := c.db.Exec(ctx, xsql.Query{
if err := c.userDB.Exec(ctx, xsql.Query{
String: fmt.Sprintf("DROP USER IF EXISTS %s", mssql.QuoteIdentifier(meta.GetExternalName(cr))),
}); err != nil {
return errors.Wrapf(err, errDropUser, meta.GetExternalName(cr))
}

if err := c.db.Exec(ctx, xsql.Query{
if err := c.loginDB.Exec(ctx, xsql.Query{
String: fmt.Sprintf("DROP LOGIN %s", mssql.QuoteIdentifier(meta.GetExternalName(cr))),
}); err != nil {
return errors.Wrapf(err, errDropLogin, meta.GetExternalName(cr))
Expand Down
Loading