github.com/jenkins-x/jx/v2@v2.1.155/pkg/kube/crds.go (about) 1 package kube 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "reflect" 7 "time" 8 9 openapi "github.com/jenkins-x/jx-api/pkg/client/openapi/all" 10 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 "k8s.io/kube-openapi/pkg/common" 13 14 "github.com/go-openapi/jsonreference" 15 16 "github.com/jenkins-x/jx-logging/pkg/log" 17 18 "github.com/go-openapi/spec" 19 "github.com/pkg/errors" 20 21 "github.com/cenkalti/backoff" 22 jenkinsio "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io" 23 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 24 apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 25 ) 26 27 // RegisterAllCRDs ensures that all Jenkins-X CRDs are registered 28 func RegisterAllCRDs(apiClient apiextensionsclientset.Interface) error { 29 err := RegisterCommitStatusCRD(apiClient) 30 if err != nil { 31 return errors.Wrap(err, "failed to register the Commit Status CRD") 32 } 33 err = RegisterExtensionCRD(apiClient) 34 if err != nil { 35 return errors.Wrap(err, "failed to register the Extension CRD") 36 } 37 err = RegisterAppCRD(apiClient) 38 if err != nil { 39 return errors.Wrap(err, "failed to register the App CRD") 40 } 41 err = RegisterPluginCRD(apiClient) 42 if err != nil { 43 return errors.Wrap(err, "failed to register the Plugin CRD") 44 } 45 err = RegisterEnvironmentRoleBindingCRD(apiClient) 46 if err != nil { 47 return errors.Wrap(err, "failed to register the Environment Role Binding CRD") 48 } 49 err = RegisterGitServiceCRD(apiClient) 50 if err != nil { 51 return errors.Wrap(err, "failed to register the Git Service CRD") 52 } 53 err = RegisterFactCRD(apiClient) 54 if err != nil { 55 return errors.Wrap(err, "failed to register the Fact CRD") 56 } 57 err = RegisterTeamCRD(apiClient) 58 if err != nil { 59 return errors.Wrap(err, "failed to register the Team CRD") 60 } 61 err = RegisterUserCRD(apiClient) 62 if err != nil { 63 return errors.Wrap(err, "failed to register the User CRD") 64 } 65 err = RegisterWorkflowCRD(apiClient) 66 if err != nil { 67 return errors.Wrap(err, "failed to register the Workflow CRD") 68 } 69 70 return RegisterPipelineCRDs(apiClient) 71 } 72 73 // RegisterPipelineCRDs ensures that all Jenkins X Pipeline related CRDs are registered 74 func RegisterPipelineCRDs(apiClient apiextensionsclientset.Interface) error { 75 err := RegisterBuildPackCRD(apiClient) 76 if err != nil { 77 return errors.Wrap(err, "failed to register the Build Pack CRD") 78 } 79 err = RegisterEnvironmentCRD(apiClient) 80 if err != nil { 81 return errors.Wrap(err, "failed to register the Environment CRD") 82 } 83 err = RegisterReleaseCRD(apiClient) 84 if err != nil { 85 return errors.Wrap(err, "failed to register the Release CRD") 86 } 87 err = RegisterPipelineActivityCRD(apiClient) 88 if err != nil { 89 return errors.Wrap(err, "failed to register the Pipeline Activity CRD") 90 } 91 err = RegisterPipelineStructureCRD(apiClient) 92 if err != nil { 93 return errors.Wrap(err, "failed to register the Pipeline Structure CRD") 94 } 95 err = RegisterPluginCRD(apiClient) 96 if err != nil { 97 return errors.Wrap(err, "failed to register the Plugin CRD") 98 } 99 err = RegisterSourceRepositoryCRD(apiClient) 100 if err != nil { 101 return errors.Wrap(err, "failed to register the SourceRepository CRD") 102 } 103 err = RegisterPipelineScheduler(apiClient) 104 if err != nil { 105 return errors.Wrap(err, "failed to register the PipelineScheduler CRD") 106 } 107 err = RegisterSourceRepositoryGroup(apiClient) 108 if err != nil { 109 return errors.Wrap(err, "failed to register the RegisterSourceRepositoryGroup CRD") 110 } 111 return nil 112 } 113 114 // RegisterEnvironmentCRD ensures that the CRD is registered for Environments 115 func RegisterEnvironmentCRD(apiClient apiextensionsclientset.Interface) error { 116 name := "environments." + jenkinsio.GroupName 117 names := &v1beta1.CustomResourceDefinitionNames{ 118 Kind: "Environment", 119 ListKind: "EnvironmentList", 120 Plural: "environments", 121 Singular: "environment", 122 ShortNames: []string{"env"}, 123 } 124 columns := []v1beta1.CustomResourceColumnDefinition{ 125 { 126 Name: "Namespace", 127 Type: "string", 128 Description: "The namespace used for the environment", 129 JSONPath: ".spec.namespace", 130 }, 131 { 132 Name: "Kind", 133 Type: "string", 134 Description: "The kind of environment", 135 JSONPath: ".spec.kind", 136 }, 137 { 138 Name: "Promotion", 139 Type: "string", 140 Description: "The strategy used for promoting to this environment", 141 JSONPath: ".spec.promotionStrategy", 142 }, 143 { 144 Name: "Order", 145 Type: "integer", 146 Description: "The order in which environments are automatically promoted", 147 JSONPath: ".spec.order", 148 }, 149 { 150 Name: "Git URL", 151 Type: "string", 152 Description: "The Git repository URL for the source of the environment configuration", 153 JSONPath: ".spec.source.url", 154 }, 155 { 156 Name: "Git Branch", 157 Type: "string", 158 Description: "The git branch for the source of the environment configuration", 159 JSONPath: ".spec.source.ref", 160 }, 161 } 162 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 163 } 164 165 // RegisterEnvironmentRoleBindingCRD ensures that the CRD is registered for Environments 166 func RegisterEnvironmentRoleBindingCRD(apiClient apiextensionsclientset.Interface) error { 167 name := "environmentrolebindings." + jenkinsio.GroupName 168 names := &v1beta1.CustomResourceDefinitionNames{ 169 Kind: "EnvironmentRoleBinding", 170 ListKind: "EnvironmentRoleBindingList", 171 Plural: "environmentrolebindings", 172 Singular: "environmentrolebinding", 173 ShortNames: []string{"envrolebindings", "envrolebinding", "envrb", "erb"}, 174 Categories: []string{"all"}, 175 } 176 columns := []v1beta1.CustomResourceColumnDefinition{} 177 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 178 } 179 180 // RegisterGitServiceCRD ensures that the CRD is registered for GitServices 181 func RegisterGitServiceCRD(apiClient apiextensionsclientset.Interface) error { 182 name := "gitservices." + jenkinsio.GroupName 183 names := &v1beta1.CustomResourceDefinitionNames{ 184 Kind: "GitService", 185 ListKind: "GitServiceList", 186 Plural: "gitservices", 187 Singular: "gitservice", 188 ShortNames: []string{"gits", "gs"}, 189 Categories: []string{"all"}, 190 } 191 columns := []v1beta1.CustomResourceColumnDefinition{ 192 { 193 Name: "Git URL", 194 Type: "string", 195 Description: "The URL of the Git repository", 196 JSONPath: ".spec.url", 197 }, 198 { 199 Name: "Kind", 200 Type: "string", 201 Description: "The kind of the Git provider", 202 JSONPath: ".spec.gitKind", 203 }, 204 } 205 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 206 } 207 208 // RegisterPipelineActivityCRD ensures that the CRD is registered for PipelineActivity 209 func RegisterPipelineActivityCRD(apiClient apiextensionsclientset.Interface) error { 210 name := "pipelineactivities." + jenkinsio.GroupName 211 names := &v1beta1.CustomResourceDefinitionNames{ 212 Kind: "PipelineActivity", 213 ListKind: "PipelineActivityList", 214 Plural: "pipelineactivities", 215 Singular: "pipelineactivity", 216 ShortNames: []string{"activity", "act", "pa"}, 217 Categories: []string{"all"}, 218 } 219 columns := []v1beta1.CustomResourceColumnDefinition{ 220 { 221 Name: "Git URL", 222 Type: "string", 223 Description: "The URL of the Git repository", 224 JSONPath: ".spec.gitUrl", 225 }, 226 { 227 Name: "Status", 228 Type: "string", 229 Description: "The status of the pipeline", 230 JSONPath: ".spec.status", 231 }, 232 } 233 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 234 } 235 236 // RegisterPipelineStructureCRD ensures that the CRD is registered for PipelineStructure 237 func RegisterPipelineStructureCRD(apiClient apiextensionsclientset.Interface) error { 238 name := "pipelinestructures." + jenkinsio.GroupName 239 names := &v1beta1.CustomResourceDefinitionNames{ 240 Kind: "PipelineStructure", 241 ListKind: "PipelineStructureList", 242 Plural: "pipelinestructures", 243 Singular: "pipelinestructure", 244 ShortNames: []string{"structure", "ps"}, 245 Categories: []string{"all"}, 246 } 247 columns := []v1beta1.CustomResourceColumnDefinition{} 248 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 249 } 250 251 // RegisterFactCRD ensures that the CRD is registered for Fact 252 func RegisterFactCRD(apiClient apiextensionsclientset.Interface) error { 253 name := "facts." + jenkinsio.GroupName 254 names := &v1beta1.CustomResourceDefinitionNames{ 255 Kind: "Fact", 256 ListKind: "FactList", 257 Plural: "facts", 258 Singular: "fact", 259 ShortNames: []string{"fact"}, 260 Categories: []string{"all"}, 261 } 262 columns := []v1beta1.CustomResourceColumnDefinition{ 263 { 264 Name: "Name", 265 Type: "string", 266 Description: "The name of the fact", 267 JSONPath: ".spec.name", 268 }, 269 { 270 Name: "Type", 271 Type: "string", 272 Description: "The type of the fact", 273 JSONPath: ".spec.factType", 274 }, 275 } 276 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 277 } 278 279 // RegisterExtensionCRD ensures that the CRD is registered for Extension 280 func RegisterExtensionCRD(apiClient apiextensionsclientset.Interface) error { 281 name := "extensions." + jenkinsio.GroupName 282 names := &v1beta1.CustomResourceDefinitionNames{ 283 Kind: "Extension", 284 ListKind: "ExtensionList", 285 Plural: "extensions", 286 Singular: "extensions", 287 ShortNames: []string{"extension", "ext"}, 288 Categories: []string{"all"}, 289 } 290 columns := []v1beta1.CustomResourceColumnDefinition{ 291 { 292 Name: "Name", 293 Type: "string", 294 Description: "The name of the extension", 295 JSONPath: ".spec.name", 296 }, 297 { 298 Name: "Description", 299 Type: "string", 300 Description: "A description of the extension", 301 JSONPath: ".spec.description", 302 }, 303 } 304 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 305 } 306 307 // RegisterBuildPackCRD ensures that the CRD is registered for BuildPack 308 func RegisterBuildPackCRD(apiClient apiextensionsclientset.Interface) error { 309 name := "buildpacks." + jenkinsio.GroupName 310 names := &v1beta1.CustomResourceDefinitionNames{ 311 Kind: "BuildPack", 312 ListKind: "BuildPackList", 313 Plural: "buildpacks", 314 Singular: "buildpack", 315 ShortNames: []string{"bp"}, 316 Categories: []string{"all"}, 317 } 318 columns := []v1beta1.CustomResourceColumnDefinition{ 319 { 320 Name: "LABEL", 321 Type: "string", 322 Description: "The label of the BuildPack", 323 JSONPath: ".spec.Label", 324 }, 325 { 326 Name: "GIT URL", 327 Type: "string", 328 Description: "The Git URL of the BuildPack", 329 JSONPath: ".spec.gitUrl", 330 }, 331 { 332 Name: "Git Ref", 333 Type: "string", 334 Description: "The Git REf of the BuildPack", 335 JSONPath: ".spec.gitRef", 336 }, 337 } 338 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 339 } 340 341 // RegisterAppCRD ensures that the CRD is registered for App 342 func RegisterAppCRD(apiClient apiextensionsclientset.Interface) error { 343 name := "apps." + jenkinsio.GroupName 344 names := &v1beta1.CustomResourceDefinitionNames{ 345 Kind: "App", 346 ListKind: "AppList", 347 Plural: "apps", 348 Singular: "app", 349 ShortNames: []string{"app"}, 350 Categories: []string{"all"}, 351 } 352 columns := []v1beta1.CustomResourceColumnDefinition{} 353 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 354 } 355 356 // RegisterPipelineScheduler ensures that the CRD is registered for App 357 func RegisterPipelineScheduler(apiClient apiextensionsclientset.Interface) error { 358 name := "schedulers." + jenkinsio.GroupName 359 names := &v1beta1.CustomResourceDefinitionNames{ 360 Kind: "Scheduler", 361 ListKind: "SchedulerList", 362 Plural: "schedulers", 363 Singular: "scheduler", 364 ShortNames: []string{"scheduler"}, 365 Categories: []string{"all"}, 366 } 367 columns := []v1beta1.CustomResourceColumnDefinition{} 368 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 369 } 370 371 // RegisterSourceRepositoryGroup ensures that the CRD is registered for App 372 func RegisterSourceRepositoryGroup(apiClient apiextensionsclientset.Interface) error { 373 name := "sourcerepositorygroups." + jenkinsio.GroupName 374 names := &v1beta1.CustomResourceDefinitionNames{ 375 Kind: "SourceRepositoryGroup", 376 ListKind: "SourceRepositoryGroupList", 377 Plural: "sourcerepositorygroups", 378 Singular: "sourcerepositorygroup", 379 ShortNames: []string{"srg"}, 380 Categories: []string{"all"}, 381 } 382 columns := []v1beta1.CustomResourceColumnDefinition{ 383 { 384 Name: "Scheduler", 385 Type: "string", 386 Description: "The pipeline scheduler used by the source repository group", 387 JSONPath: ".spec.scheduler.name", 388 }, 389 } 390 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 391 } 392 393 // RegisterSourceRepositoryCRD ensures that the CRD is registered for Applications 394 func RegisterSourceRepositoryCRD(apiClient apiextensionsclientset.Interface) error { 395 name := "sourcerepositories." + jenkinsio.GroupName 396 names := &v1beta1.CustomResourceDefinitionNames{ 397 Kind: "SourceRepository", 398 ListKind: "SourceRepositoryList", 399 Plural: "sourcerepositories", 400 Singular: "sourcerepository", 401 ShortNames: []string{"sourcerepo", "srcrepo", "sr"}, 402 Categories: []string{"all"}, 403 } 404 columns := []v1beta1.CustomResourceColumnDefinition{ 405 { 406 Name: "URL", 407 Type: "string", 408 Description: "The URL of the git repository", 409 JSONPath: ".spec.url", 410 }, 411 { 412 Name: "Description", 413 Type: "string", 414 Description: "A description of the source code repository - non-functional user-data", 415 JSONPath: ".spec.description", 416 }, 417 } 418 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 419 } 420 421 // RegisterPluginCRD ensures that the CRD is registered for Plugin 422 func RegisterPluginCRD(apiClient apiextensionsclientset.Interface) error { 423 name := "plugins." + jenkinsio.GroupName 424 names := &v1beta1.CustomResourceDefinitionNames{ 425 Kind: "Plugin", 426 ListKind: "PluginList", 427 Plural: "plugins", 428 Singular: "plugin", 429 Categories: []string{"all"}, 430 } 431 columns := []v1beta1.CustomResourceColumnDefinition{ 432 { 433 Name: "Name", 434 Type: "string", 435 Description: "The name of the plugin", 436 JSONPath: ".spec.name", 437 }, 438 { 439 Name: "Description", 440 Type: "string", 441 Description: "A description of the plugin", 442 JSONPath: ".spec.description", 443 }, 444 } 445 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 446 } 447 448 // RegisterCommitStatusCRD ensures that the CRD is registered for CommitStatus 449 func RegisterCommitStatusCRD(apiClient apiextensionsclientset.Interface) error { 450 name := "commitstatuses." + jenkinsio.GroupName 451 names := &v1beta1.CustomResourceDefinitionNames{ 452 Kind: "CommitStatus", 453 ListKind: "CommitStatusList", 454 Plural: "commitstatuses", 455 Singular: "commitstatus", 456 ShortNames: []string{"commitstatus"}, 457 Categories: []string{"all"}, 458 } 459 columns := []v1beta1.CustomResourceColumnDefinition{} 460 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 461 } 462 463 // RegisterReleaseCRD ensures that the CRD is registered for Release 464 func RegisterReleaseCRD(apiClient apiextensionsclientset.Interface) error { 465 name := "releases." + jenkinsio.GroupName 466 names := &v1beta1.CustomResourceDefinitionNames{ 467 Kind: "Release", 468 ListKind: "ReleaseList", 469 Plural: "releases", 470 Singular: "release", 471 ShortNames: []string{"rel"}, 472 Categories: []string{"all"}, 473 } 474 columns := []v1beta1.CustomResourceColumnDefinition{ 475 { 476 Name: "Name", 477 Type: "string", 478 Description: "The name of the Release", 479 JSONPath: ".spec.name", 480 }, 481 { 482 Name: "Version", 483 Type: "string", 484 Description: "The version number of the Release", 485 JSONPath: ".spec.version", 486 }, 487 { 488 Name: "Git URL", 489 Type: "string", 490 Description: "The URL of the Git repository", 491 JSONPath: ".spec.gitHttpUrl", 492 }, 493 } 494 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 495 } 496 497 // RegisterUserCRD ensures that the CRD is registered for User 498 func RegisterUserCRD(apiClient apiextensionsclientset.Interface) error { 499 name := "users." + jenkinsio.GroupName 500 names := &v1beta1.CustomResourceDefinitionNames{ 501 Kind: "User", 502 ListKind: "UserList", 503 Plural: "users", 504 Singular: "user", 505 ShortNames: []string{"usr"}, 506 Categories: []string{"all"}, 507 } 508 columns := []v1beta1.CustomResourceColumnDefinition{ 509 { 510 Name: "Name", 511 Type: "string", 512 Description: "The name of the user", 513 JSONPath: ".spec.name", 514 }, 515 { 516 Name: "Email", 517 Type: "string", 518 Description: "The email address of the user", 519 JSONPath: ".spec.email", 520 }, 521 } 522 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 523 } 524 525 // RegisterTeamCRD ensures that the CRD is registered for Team 526 func RegisterTeamCRD(apiClient apiextensionsclientset.Interface) error { 527 name := "teams." + jenkinsio.GroupName 528 names := &v1beta1.CustomResourceDefinitionNames{ 529 Kind: "Team", 530 ListKind: "TeamList", 531 Plural: "teams", 532 Singular: "team", 533 ShortNames: []string{"tm"}, 534 Categories: []string{"all"}, 535 } 536 columns := []v1beta1.CustomResourceColumnDefinition{ 537 { 538 Name: "Kind", 539 Type: "string", 540 Description: "The kind of Team", 541 JSONPath: ".spec.kind", 542 }, 543 { 544 Name: "Status", 545 Type: "string", 546 Description: "The provision status of the Team", 547 JSONPath: ".status.provisionStatus", 548 }, 549 } 550 551 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 552 } 553 554 // RegisterWorkflowCRD ensures that the CRD is registered for Environments 555 func RegisterWorkflowCRD(apiClient apiextensionsclientset.Interface) error { 556 name := "workflows." + jenkinsio.GroupName 557 names := &v1beta1.CustomResourceDefinitionNames{ 558 Kind: "Workflow", 559 ListKind: "WorkflowList", 560 Plural: "workflows", 561 Singular: "workflow", 562 ShortNames: []string{"flow"}, 563 Categories: []string{"all"}, 564 } 565 columns := []v1beta1.CustomResourceColumnDefinition{} 566 return RegisterCRD(apiClient, name, names, columns, jenkinsio.GroupName, jenkinsio.Package, jenkinsio.Version) 567 } 568 569 // RegisterCRD allows new custom resources to be registered using apiClient under a particular name. 570 // Various forms of the name are provided using names. In Kubernetes 1.11 571 // and later a custom display format for kubectl is used, which is specified using columns. 572 func RegisterCRD(apiClient apiextensionsclientset.Interface, name string, 573 names *v1beta1.CustomResourceDefinitionNames, columns []v1beta1.CustomResourceColumnDefinition, groupName string, 574 pkg string, version string) error { 575 //"github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1.PipelineActivity": 576 schemaPath := fmt.Sprintf("%s/%s/%s.%s", pkg, groupName, version, names.Kind) 577 578 schema, err := getOpenAPISchema(schemaPath) 579 if err != nil { 580 return errors.Wrapf(err, "error generating OpenAPI Schema for %s", schemaPath) 581 } 582 crd := &v1beta1.CustomResourceDefinition{ 583 ObjectMeta: metav1.ObjectMeta{ 584 Name: name, 585 }, 586 Spec: v1beta1.CustomResourceDefinitionSpec{ 587 Group: groupName, 588 Version: version, 589 Scope: v1beta1.NamespaceScoped, 590 Names: *names, 591 AdditionalPrinterColumns: columns, 592 Validation: &v1beta1.CustomResourceValidation{ 593 OpenAPIV3Schema: schema, 594 }, 595 }, 596 } 597 598 return register(apiClient, name, crd) 599 } 600 601 func getOpenAPIDefinitions(name string, ref common.ReferenceCallback) *common.OpenAPIDefinition { 602 if def, ok := openapi.GetOpenAPIDefinitions(ref)[name]; ok { 603 return &def 604 } 605 return nil 606 } 607 608 func getOpenAPISchema(defName string) (*v1beta1.JSONSchemaProps, error) { 609 refCallBack := func(path string) spec.Ref { 610 ref, err := jsonreference.New(path) 611 if err != nil { 612 log.Logger().Warnf("Error resolving ref %s %v", path, err) 613 } 614 return spec.Ref{ 615 Ref: ref, 616 } 617 } 618 if def := getOpenAPIDefinitions(defName, refCallBack); def != nil { 619 // resolve references 620 schema, err := FixSchema(def.Schema, refCallBack) 621 if err != nil { 622 return nil, err 623 } 624 // Unfortunately the schema is generated into one type, and the validation takes another type. 625 // However both define the OpenAPI v3 data structures and are compatible, so we i 626 bytes, err := json.Marshal(schema) 627 if err != nil { 628 return nil, err 629 } 630 def1 := v1beta1.JSONSchemaProps{} 631 err = json.Unmarshal(bytes, &def1) 632 if err != nil { 633 return nil, err 634 } 635 return &def1, nil 636 } 637 return nil, nil 638 } 639 640 // FixSchema walks the schema and automatically fixes it up to be better supported by Kubernetes. 641 // Current automatic fixes are: 642 // * resolving $ref 643 // * remove unresolved $ref 644 // * clear additionalProperties (this is unsupported in older kubernetes, when we drop them, 645 // we can investigate adding support, for now use patternProperties) 646 // 647 // as these are all unsupported 648 func FixSchema(schema spec.Schema, ref common.ReferenceCallback) (spec.Schema, error) { 649 if schema.Type.Contains("object") { 650 for k, v := range schema.Properties { 651 resolved, err := FixSchema(v, ref) 652 if err != nil { 653 return schema, err 654 } 655 schema.Properties[k] = resolved 656 } 657 schema.AdditionalProperties = nil 658 } else if schema.Type.Contains("array") { 659 if schema.Items.Len() == 1 { 660 resolved, err := FixSchema(*schema.Items.Schema, ref) 661 if err != nil { 662 return schema, err 663 } 664 schema.Items.Schema = &resolved 665 } else { 666 result := make([]spec.Schema, 0) 667 for _, v := range schema.Items.Schemas { 668 resolved, err := FixSchema(v, ref) 669 if err != nil { 670 return schema, err 671 } 672 result = append(result, resolved) 673 } 674 schema.Items.Schemas = result 675 } 676 677 } else if path := schema.Ref.String(); path != "" { 678 def := getOpenAPIDefinitions(path, ref) 679 if def != nil { 680 return FixSchema(def.Schema, ref) 681 } 682 // return an empty schema if we can't resolve 683 return spec.Schema{}, nil 684 } 685 return schema, nil 686 } 687 688 func register(apiClient apiextensionsclientset.Interface, name string, crd *v1beta1.CustomResourceDefinition) error { 689 crdResources := apiClient.ApiextensionsV1beta1().CustomResourceDefinitions() 690 691 f := func() error { 692 old, err := crdResources.Get(name, metav1.GetOptions{}) 693 if err == nil { 694 if !reflect.DeepEqual(&crd.Spec, old.Spec) { 695 old.Spec = crd.Spec 696 _, err = crdResources.Update(old) 697 if err != nil { 698 log.Logger().Infof("Error doing update to %s %v\n%v", old.Name, err, old.Spec) 699 } 700 return err 701 } 702 return nil 703 } 704 705 _, err = crdResources.Create(crd) 706 if err != nil { 707 log.Logger().Infof("Error creating %s: %v", crd.Name, err) 708 } 709 return err 710 } 711 712 exponentialBackOff := backoff.NewExponentialBackOff() 713 timeout := 60 * time.Second 714 exponentialBackOff.MaxElapsedTime = timeout 715 exponentialBackOff.Reset() 716 return backoff.Retry(f, exponentialBackOff) 717 }