diff --git a/api/swagger-spec/api.json b/api/swagger-spec/api.json index 2da21dda5fc..b21e436b15e 100644 --- a/api/swagger-spec/api.json +++ b/api/swagger-spec/api.json @@ -15,10 +15,12 @@ "nickname": "getAPIVersions", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/api/swagger-spec/apis.json b/api/swagger-spec/apis.json index 0602e220758..07d061d49b3 100644 --- a/api/swagger-spec/apis.json +++ b/api/swagger-spec/apis.json @@ -15,10 +15,12 @@ "nickname": "getAPIVersions", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/api/swagger-spec/extensions.json b/api/swagger-spec/extensions.json index 3b4acb17fbf..7bdf667192d 100644 --- a/api/swagger-spec/extensions.json +++ b/api/swagger-spec/extensions.json @@ -15,10 +15,12 @@ "nickname": "getAPIGroup", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json index cbc65ba3b86..bedb828a854 100644 --- a/api/swagger-spec/v1.json +++ b/api/swagger-spec/v1.json @@ -47,7 +47,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -122,7 +123,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -165,7 +167,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -248,7 +251,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -293,7 +297,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -370,7 +375,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -520,7 +526,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -573,7 +580,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -626,7 +634,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -681,7 +690,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -847,7 +857,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1005,7 +1016,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1050,7 +1062,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1127,7 +1140,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1277,7 +1291,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1330,7 +1345,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1383,7 +1399,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -1438,7 +1455,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1604,7 +1622,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1762,7 +1781,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1807,7 +1827,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1884,7 +1905,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2034,7 +2056,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2087,7 +2110,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2140,7 +2164,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -2195,7 +2220,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2361,7 +2387,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2511,7 +2538,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2548,7 +2576,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2617,7 +2646,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2751,7 +2781,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2796,7 +2827,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2841,7 +2873,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -2888,7 +2921,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3022,7 +3056,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3073,7 +3108,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3148,7 +3184,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3185,7 +3222,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3254,7 +3292,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3388,7 +3427,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3433,7 +3473,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3478,7 +3519,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -3525,7 +3567,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3983,7 +4026,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4066,7 +4110,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4111,7 +4156,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4188,7 +4234,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4338,7 +4385,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4391,7 +4439,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4444,7 +4493,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -4499,7 +4549,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4665,7 +4716,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4799,7 +4851,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4874,7 +4927,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4911,7 +4965,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -4980,7 +5035,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5114,7 +5170,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5159,7 +5216,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5204,7 +5262,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -5251,7 +5310,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5385,7 +5445,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5468,7 +5529,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5513,7 +5575,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5590,7 +5653,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5740,7 +5804,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5793,7 +5858,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -5846,7 +5912,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -5901,7 +5968,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -6487,7 +6555,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -6767,7 +6836,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7044,7 +7114,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7685,7 +7756,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7768,7 +7840,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7813,7 +7886,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -7890,7 +7964,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8040,7 +8115,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8093,7 +8169,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8146,7 +8223,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -8201,7 +8279,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8367,7 +8446,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8525,7 +8605,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8570,7 +8651,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8647,7 +8729,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8797,7 +8880,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8850,7 +8934,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -8903,7 +8988,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -8958,7 +9044,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9124,7 +9211,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9258,7 +9346,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9341,7 +9430,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9386,7 +9476,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9463,7 +9554,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9613,7 +9705,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9666,7 +9759,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9719,7 +9813,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -9774,7 +9869,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -9940,7 +10036,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10074,7 +10171,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10157,7 +10255,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10202,7 +10301,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10279,7 +10379,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10429,7 +10530,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10482,7 +10584,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10535,7 +10638,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -10590,7 +10694,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10756,7 +10861,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10914,7 +11020,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -10959,7 +11066,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11036,7 +11144,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11186,7 +11295,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11239,7 +11349,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11292,7 +11403,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -11347,7 +11459,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11513,7 +11626,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11671,7 +11785,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11716,7 +11831,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11850,7 +11966,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11903,7 +12020,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -11956,7 +12074,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -12003,7 +12122,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -12589,7 +12709,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -12683,10 +12804,12 @@ "nickname": "getAPIResources", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/api/swagger-spec/v1beta1.json b/api/swagger-spec/v1beta1.json index c6869483de2..ce8b4a53964 100644 --- a/api/swagger-spec/v1beta1.json +++ b/api/swagger-spec/v1beta1.json @@ -79,7 +79,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -124,7 +125,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -201,7 +203,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -351,7 +354,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -404,7 +408,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -457,7 +462,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -512,7 +518,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -678,7 +685,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -836,7 +844,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -881,7 +890,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -958,7 +968,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1108,7 +1119,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1161,7 +1173,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1214,7 +1227,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -1269,7 +1283,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1435,7 +1450,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1569,7 +1585,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1652,7 +1669,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1697,7 +1715,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1774,7 +1793,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1924,7 +1944,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -1977,7 +1998,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2030,7 +2052,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -2085,7 +2108,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2251,7 +2275,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2385,7 +2410,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2468,7 +2494,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2513,7 +2540,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2590,7 +2618,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2740,7 +2769,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2793,7 +2823,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -2846,7 +2877,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -2901,7 +2933,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3067,7 +3100,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3201,7 +3235,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3252,7 +3287,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3305,7 +3341,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "*/*" @@ -3358,7 +3395,8 @@ } ], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ "application/json-patch+json", @@ -3379,10 +3417,12 @@ "nickname": "getAPIResources", "parameters": [], "produces": [ - "application/json" + "application/json", + "application/yaml" ], "consumes": [ - "application/json" + "application/json", + "application/yaml" ] } ] diff --git a/cmd/gendeepcopy/deep_copy.go b/cmd/gendeepcopy/deep_copy.go index a10bfe3a011..70672df3a36 100644 --- a/cmd/gendeepcopy/deep_copy.go +++ b/cmd/gendeepcopy/deep_copy.go @@ -61,7 +61,7 @@ func pkgPath(group, version string) string { group = "api" } gv := group - if version != "" { + if version != "__internal" { gv = path.Join(group, version) } switch { @@ -104,7 +104,7 @@ func main() { } else { pkgname = gv.Group } - if len(gv.Version) != 0 { + if len(gv.Version) != 0 && gv.Version != kruntime.APIVersionInternal { pkgname = gv.Version } diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index d9f29a6ae59..ff07fd38e99 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -26,7 +26,7 @@ import ( "net/http/httptest" "os" "reflect" - "runtime" + gruntime "runtime" "strconv" "strings" "sync" @@ -52,6 +52,7 @@ import ( kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/master" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" utilnet "k8s.io/kubernetes/pkg/util/net" "k8s.io/kubernetes/pkg/util/sets" @@ -444,7 +445,7 @@ func runReplicationControllerTest(c *client.Client) { glog.Infof("Done reading config file, took %v", time.Since(t)) t = time.Now() var controller api.ReplicationController - if err := api.Scheme.DecodeInto(data, &controller); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &controller); err != nil { glog.Fatalf("Unexpected error: %v", err) } @@ -958,7 +959,7 @@ func addFlags(fs *pflag.FlagSet) { } func main() { - runtime.GOMAXPROCS(runtime.NumCPU()) + gruntime.GOMAXPROCS(gruntime.NumCPU()) addFlags(pflag.CommandLine) util.InitFlags() diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index eb847e5ff75..71cdf66cca4 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -34,7 +34,6 @@ import ( "k8s.io/kubernetes/cmd/kube-apiserver/app/options" "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" apiutil "k8s.io/kubernetes/pkg/api/util" "k8s.io/kubernetes/pkg/apimachinery/registered" @@ -48,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/genericapiserver" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/master" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/storage" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" @@ -82,9 +82,9 @@ func verifyClusterIPFlags(s *options.APIServer) { } } -type newEtcdFunc func([]string, meta.VersionInterfacesFunc, string, string) (storage.Interface, error) +type newEtcdFunc func([]string, runtime.NegotiatedSerializer, string, string) (storage.Interface, error) -func newEtcd(etcdServerList []string, interfacesFunc meta.VersionInterfacesFunc, storageGroupVersionString, pathPrefix string) (etcdStorage storage.Interface, err error) { +func newEtcd(etcdServerList []string, ns runtime.NegotiatedSerializer, storageGroupVersionString, pathPrefix string) (etcdStorage storage.Interface, err error) { if storageGroupVersionString == "" { return etcdStorage, fmt.Errorf("storageVersion is required to create a etcd storage") } @@ -96,11 +96,11 @@ func newEtcd(etcdServerList []string, interfacesFunc meta.VersionInterfacesFunc, var storageConfig etcdstorage.EtcdConfig storageConfig.ServerList = etcdServerList storageConfig.Prefix = pathPrefix - versionedInterface, err := interfacesFunc(storageVersion) - if err != nil { - return nil, err + s, ok := ns.SerializerForMediaType("application/json", nil) + if !ok { + return nil, fmt.Errorf("unable to find serializer for JSON") } - storageConfig.Codec = versionedInterface.Codec + storageConfig.Codec = runtime.NewCodec(ns.EncoderForVersion(s, storageVersion), ns.DecoderToVersion(s, unversioned.GroupVersion{Group: storageVersion.Group, Version: runtime.APIVersionInternal})) return storageConfig.NewStorage() } @@ -149,7 +149,7 @@ func updateEtcdOverrides(overrides []string, storageVersions map[string]string, } servers := strings.Split(tokens[1], ";") - etcdOverrideStorage, err := newEtcdFn(servers, apigroup.InterfacesFor, storageVersions[apigroup.GroupVersion.Group], prefix) + etcdOverrideStorage, err := newEtcdFn(servers, api.Codecs, storageVersions[apigroup.GroupVersion.Group], prefix) if err != nil { glog.Fatalf("Invalid storage version or misconfigured etcd for %s: %v", tokens[0], err) } @@ -260,7 +260,7 @@ func Run(s *options.APIServer) error { if _, found := storageVersions[legacyV1Group.GroupVersion.Group]; !found { glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", legacyV1Group.GroupVersion.Group, storageVersions) } - etcdStorage, err := newEtcd(s.EtcdServerList, legacyV1Group.InterfacesFor, storageVersions[legacyV1Group.GroupVersion.Group], s.EtcdPathPrefix) + etcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageVersions[legacyV1Group.GroupVersion.Group], s.EtcdPathPrefix) if err != nil { glog.Fatalf("Invalid storage version or misconfigured etcd: %v", err) } @@ -274,7 +274,7 @@ func Run(s *options.APIServer) error { if _, found := storageVersions[expGroup.GroupVersion.Group]; !found { glog.Fatalf("Couldn't find the storage version for group: %q in storageVersions: %v", expGroup.GroupVersion.Group, storageVersions) } - expEtcdStorage, err := newEtcd(s.EtcdServerList, expGroup.InterfacesFor, storageVersions[expGroup.GroupVersion.Group], s.EtcdPathPrefix) + expEtcdStorage, err := newEtcd(s.EtcdServerList, api.Codecs, storageVersions[expGroup.GroupVersion.Group], s.EtcdPathPrefix) if err != nil { glog.Fatalf("Invalid extensions storage version or misconfigured etcd: %v", err) } @@ -381,6 +381,7 @@ func Run(s *options.APIServer) error { ProxyTLSClientConfig: proxyTLSClientConfig, ServiceNodePortRange: s.ServiceNodePortRange, KubernetesServiceNodePort: s.KubernetesServiceNodePort, + Serializer: api.Codecs, }, EnableCoreControllers: true, EventTTL: s.EventTTL, diff --git a/cmd/kube-apiserver/app/server_test.go b/cmd/kube-apiserver/app/server_test.go index 165256dfb3a..ed98edbf5e8 100644 --- a/cmd/kube-apiserver/app/server_test.go +++ b/cmd/kube-apiserver/app/server_test.go @@ -24,9 +24,9 @@ import ( "k8s.io/kubernetes/cmd/kube-apiserver/app/options" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/genericapiserver" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" ) @@ -133,7 +133,7 @@ func TestUpdateEtcdOverrides(t *testing.T) { } for _, test := range testCases { - newEtcd := func(serverList []string, _ meta.VersionInterfacesFunc, _, _ string) (storage.Interface, error) { + newEtcd := func(serverList []string, _ runtime.NegotiatedSerializer, _, _ string) (storage.Interface, error) { if !reflect.DeepEqual(test.servers, serverList) { t.Errorf("unexpected server list, expected: %#v, got: %#v", test.servers, serverList) } diff --git a/cmd/libs/go2idl/client-gen/generators/generator-for-group.go b/cmd/libs/go2idl/client-gen/generators/generator-for-group.go index 22a4ed18c2f..75b3edc51ea 100644 --- a/cmd/libs/go2idl/client-gen/generators/generator-for-group.go +++ b/cmd/libs/go2idl/client-gen/generators/generator-for-group.go @@ -48,13 +48,14 @@ func (g *genGroup) Namers(c *generator.Context) namer.NameSystems { } func (g *genGroup) Imports(c *generator.Context) (imports []string) { - return append(g.imports.ImportLines(), "fmt") + return g.imports.ImportLines() } func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { sw := generator.NewSnippetWriter(w, c, "$", "$") const pkgUnversioned = "k8s.io/kubernetes/pkg/client/unversioned" const pkgRegistered = "k8s.io/kubernetes/pkg/apimachinery/registered" + const pkgAPI = "k8s.io/kubernetes/pkg/api" apiPath := func(group string) string { if group == "legacy" { return `"/api"` @@ -81,6 +82,7 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer "latestGroup": c.Universe.Variable(types.Name{Package: pkgRegistered, Name: "Group"}), "GroupOrDie": c.Universe.Variable(types.Name{Package: pkgRegistered, Name: "GroupOrDie"}), "apiPath": apiPath(g.group), + "latestCodecs": c.Universe.Variable(types.Name{Package: pkgAPI, Name: "Codecs"}), } sw.Do(groupInterfaceTemplate, m) sw.Do(groupClientTemplate, m) @@ -181,12 +183,7 @@ func setConfigDefaults(config *$.Config|raw$) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("$.Group$ API version '%s' is not recognized (valid values: %s)", - config.GroupVersion, g.GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = $.latestCodecs|raw$.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/install/install.go b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/install/install.go index 9917557830c..90f76a025e5 100644 --- a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/install/install.go +++ b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/install/install.go @@ -68,7 +68,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -97,7 +96,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/register.go b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/register.go index ec99f0cfe45..f5f7b3192b1 100644 --- a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/register.go +++ b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/register.go @@ -22,7 +22,7 @@ import ( "k8s.io/kubernetes/pkg/runtime" ) -var SchemeGroupVersion = unversioned.GroupVersion{Group: "testgroup", Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: "testgroup", Version: runtime.APIVersionInternal} func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. diff --git a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1/register.go b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1/register.go index 90dbbbfeb4f..9f98cf4c234 100644 --- a/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1/register.go +++ b/cmd/libs/go2idl/client-gen/testdata/apis/testgroup/v1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" @@ -25,8 +24,6 @@ import ( var SchemeGroupVersion = unversioned.GroupVersion{Group: "testgroup", Version: "v1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. addKnownTypes(scheme) diff --git a/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_client.go b/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_client.go index b011ed8a668..a80e27299f3 100644 --- a/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_client.go +++ b/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_client.go @@ -17,7 +17,7 @@ limitations under the License. package unversioned import ( - "fmt" + api "k8s.io/kubernetes/pkg/api" registered "k8s.io/kubernetes/pkg/apimachinery/registered" unversioned "k8s.io/kubernetes/pkg/client/unversioned" ) @@ -79,12 +79,7 @@ func setConfigDefaults(config *unversioned.Config) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("Testgroup API version '%s' is not recognized (valid values: %s)", - config.GroupVersion, g.GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_test.go b/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_test.go index fbb577e89f3..af504efad1d 100644 --- a/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_test.go +++ b/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned/testgroup_test.go @@ -40,7 +40,7 @@ func init() { return } testapi.Groups[testgroup.SchemeGroupVersion.Group] = testapi.NewTestGroup( - unversioned.GroupVersion{Group: testgroup.SchemeGroupVersion.Group, Version: registered.GroupOrDie(testgroup.SchemeGroupVersion.Group).GroupVersion.Version}, + registered.GroupOrDie(testgroup.SchemeGroupVersion.Group).GroupVersion, testgroup.SchemeGroupVersion) testHelper = testapi.Groups[testgroup.SchemeGroupVersion.Group] } diff --git a/contrib/mesos/pkg/executor/executor.go b/contrib/mesos/pkg/executor/executor.go index 5270a4ee29a..3edb92697e0 100644 --- a/contrib/mesos/pkg/executor/executor.go +++ b/contrib/mesos/pkg/executor/executor.go @@ -42,6 +42,7 @@ import ( client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/dockertools" + kruntime "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" ) @@ -343,7 +344,7 @@ func (k *Executor) LaunchTask(driver bindings.ExecutorDriver, taskInfo *mesos.Ta return } - obj, err := api.Codec.Decode(taskInfo.GetData()) + obj, err := kruntime.Decode(api.Codecs.UniversalDecoder(), taskInfo.GetData()) if err != nil { log.Errorf("failed to extract yaml data from the taskInfo.data %v", err) k.sendStatus(driver, newStatus(taskInfo.GetTaskId(), mesos.TaskState_TASK_FAILED, diff --git a/contrib/mesos/pkg/executor/executor_test.go b/contrib/mesos/pkg/executor/executor_test.go index 139042a7a49..9e07281531c 100644 --- a/contrib/mesos/pkg/executor/executor_test.go +++ b/contrib/mesos/pkg/executor/executor_test.go @@ -211,7 +211,7 @@ func TestExecutorLaunchAndKillTask(t *testing.T) { taskInfo, err := podTask.BuildTaskInfo() assert.Equal(t, nil, err, "must be able to build task info") - data, err := testapi.Default.Codec().Encode(pod) + data, err := runtime.Encode(testapi.Default.Codec(), pod) assert.Equal(t, nil, err, "must be able to encode a pod's spec data") taskInfo.Data = data @@ -410,7 +410,7 @@ func TestExecutorFrameworkMessage(t *testing.T) { taskInfo, err := podTask.BuildTaskInfo() assert.Equal(t, nil, err, "must be able to build task info") - data, _ := testapi.Default.Codec().Encode(pod) + data, _ := runtime.Encode(testapi.Default.Codec(), pod) taskInfo.Data = data mockDriver.On( diff --git a/contrib/mesos/pkg/podutil/gzip.go b/contrib/mesos/pkg/podutil/gzip.go index 8778326d092..ba0f1cd1b72 100644 --- a/contrib/mesos/pkg/podutil/gzip.go +++ b/contrib/mesos/pkg/podutil/gzip.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/api" _ "k8s.io/kubernetes/pkg/api/install" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" ) func Gzip(pods <-chan *api.Pod) ([]byte, error) { @@ -32,7 +33,7 @@ func Gzip(pods <-chan *api.Pod) ([]byte, error) { } func gzipList(list *api.PodList) ([]byte, error) { - raw, err := v1.Codec.Encode(list) + raw, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), list) if err != nil { return nil, err } @@ -68,7 +69,7 @@ func gunzipList(gzipped []byte) (*api.PodList, error) { return nil, err } - obj, err := api.Scheme.Decode(raw) + obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), raw) if err != nil { return nil, err } diff --git a/contrib/mesos/pkg/podutil/io.go b/contrib/mesos/pkg/podutil/io.go index e21c22f7ab7..4eb563a2f9c 100644 --- a/contrib/mesos/pkg/podutil/io.go +++ b/contrib/mesos/pkg/podutil/io.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/validation" + "k8s.io/kubernetes/pkg/runtime" utilyaml "k8s.io/kubernetes/pkg/util/yaml" ) @@ -41,7 +42,7 @@ func WriteToDir(pods <-chan *api.Pod, destDir string) error { log.Warningf("skipping static pod %s/%s that had no filename", p.Namespace, p.Name) continue } - raw, err := v1.Codec.Encode(p) + raw, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), p) if err != nil { log.Errorf("failed to encode static pod as v1 object: %v", err) continue @@ -105,7 +106,7 @@ func tryDecodeSinglePod(data []byte) (parsed bool, pod *api.Pod, err error) { if err != nil { return false, nil, err } - obj, err := api.Scheme.Decode(json) + obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), json) if err != nil { return false, pod, err } diff --git a/contrib/mesos/pkg/scheduler/components/binder/binder.go b/contrib/mesos/pkg/scheduler/components/binder/binder.go index 55425316376..4cb39fa529c 100644 --- a/contrib/mesos/pkg/scheduler/components/binder/binder.go +++ b/contrib/mesos/pkg/scheduler/components/binder/binder.go @@ -26,6 +26,8 @@ import ( annotation "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/meta" "k8s.io/kubernetes/contrib/mesos/pkg/scheduler/podtask" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" ) type Binder interface { @@ -150,7 +152,7 @@ func (b *binder) prepareTaskForLaunch(ctx api.Context, machine string, task *pod // the kubelet-executor uses this to instantiate the pod log.V(3).Infof("prepared pod spec: %+v", pod) - data, err := api.Codec.Encode(&pod) + data, err := runtime.Encode(api.Codecs.LegacyCodec(v1.SchemeGroupVersion), &pod) if err != nil { log.V(2).Infof("Failed to marshal the pod spec: %v", err) return err diff --git a/docs/api-reference/extensions/v1beta1/operations.html b/docs/api-reference/extensions/v1beta1/operations.html index f50ffd3cb8e..f2edc2e3936 100755 --- a/docs/api-reference/extensions/v1beta1/operations.html +++ b/docs/api-reference/extensions/v1beta1/operations.html @@ -406,6 +406,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -416,6 +419,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -553,6 +559,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -690,6 +699,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -827,6 +839,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -964,6 +979,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1109,6 +1127,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1254,6 +1275,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1367,6 +1391,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1496,6 +1523,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1617,6 +1647,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1738,6 +1771,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1865,6 +1901,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2010,6 +2049,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2155,6 +2197,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2268,6 +2313,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2397,6 +2445,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2518,6 +2569,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2639,6 +2693,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2766,6 +2823,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2887,6 +2947,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3032,6 +3095,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3177,6 +3243,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3290,6 +3359,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3419,6 +3491,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3540,6 +3615,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3661,6 +3739,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3788,6 +3869,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3909,6 +3993,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4054,6 +4141,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4199,6 +4289,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4312,6 +4405,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4441,6 +4537,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4562,6 +4661,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4683,6 +4785,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4810,6 +4915,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4931,6 +5039,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5044,6 +5155,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5165,6 +5279,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5292,6 +5409,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -7051,7 +7171,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; } diff --git a/docs/api-reference/v1/operations.html b/docs/api-reference/v1/operations.html index 532f1f224ab..a8bc0828e79 100755 --- a/docs/api-reference/v1/operations.html +++ b/docs/api-reference/v1/operations.html @@ -406,6 +406,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -416,6 +419,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -553,6 +559,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -658,6 +667,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -795,6 +807,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -932,6 +947,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1069,6 +1087,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1206,6 +1227,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1343,6 +1367,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1448,6 +1475,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1561,6 +1591,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1706,6 +1739,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1851,6 +1887,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -1964,6 +2003,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2093,6 +2135,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2214,6 +2259,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2335,6 +2383,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2462,6 +2513,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2607,6 +2661,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2752,6 +2809,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2865,6 +2925,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -2994,6 +3057,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3115,6 +3181,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3236,6 +3305,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3363,6 +3435,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3508,6 +3583,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3653,6 +3731,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3766,6 +3847,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -3895,6 +3979,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4016,6 +4103,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4137,6 +4227,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4264,6 +4357,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4409,6 +4505,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4554,6 +4653,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4667,6 +4769,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4796,6 +4901,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -4917,6 +5025,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5038,6 +5149,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5165,6 +5279,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5286,6 +5403,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5431,6 +5551,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5576,6 +5699,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5689,6 +5815,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5818,6 +5947,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -5939,6 +6071,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -6060,6 +6195,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -6187,6 +6325,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -6598,6 +6739,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -7081,6 +7225,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8348,6 +8495,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8493,6 +8643,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8638,6 +8791,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8751,6 +8907,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -8880,6 +9039,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9001,6 +9163,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9122,6 +9287,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9249,6 +9417,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9394,6 +9565,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9539,6 +9713,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9652,6 +9829,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9781,6 +9961,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -9902,6 +10085,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10023,6 +10209,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10150,6 +10339,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10271,6 +10463,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10416,6 +10611,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10561,6 +10759,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10674,6 +10875,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10803,6 +11007,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -10924,6 +11131,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11045,6 +11255,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11172,6 +11385,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11293,6 +11509,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11438,6 +11657,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11583,6 +11805,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11696,6 +11921,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11825,6 +12053,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -11946,6 +12177,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12067,6 +12301,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12194,6 +12431,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12339,6 +12579,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12484,6 +12727,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12597,6 +12843,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12726,6 +12975,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12847,6 +13099,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -12968,6 +13223,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13095,6 +13353,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13240,6 +13501,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13353,6 +13617,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13466,6 +13733,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13587,6 +13857,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13700,6 +13973,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13827,6 +14103,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -13948,6 +14227,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14061,6 +14343,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14174,6 +14459,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14293,6 +14581,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14406,6 +14697,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14519,6 +14813,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14656,6 +14953,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14793,6 +15093,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -14898,6 +15201,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15019,6 +15325,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15132,6 +15441,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15245,6 +15557,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15364,6 +15679,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15477,6 +15795,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15614,6 +15935,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15751,6 +16075,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15888,6 +16215,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -15993,6 +16323,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16114,6 +16447,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16227,6 +16563,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16340,6 +16679,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16459,6 +16801,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16572,6 +16917,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16709,6 +17057,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -16846,6 +17197,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -19535,6 +19889,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -19672,6 +20029,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -19809,6 +20169,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -19946,6 +20309,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • @@ -20083,6 +20449,9 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
  • application/json

  • +
  • +

    application/yaml

    +
  • diff --git a/examples/examples_test.go b/examples/examples_test.go index 6ec81b83f12..f6920945d1b 100644 --- a/examples/examples_test.go +++ b/examples/examples_test.go @@ -421,7 +421,7 @@ func TestExampleObjectSchemas(t *testing.T) { return } if strings.Contains(name, "scheduler-policy-config") { - if err := schedulerapilatest.Codec.DecodeInto(data, expectedType); err != nil { + if err := runtime.DecodeInto(schedulerapilatest.Codec, data, expectedType); err != nil { t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(data)) return } @@ -516,14 +516,14 @@ func TestReadme(t *testing.T) { if err != nil { t.Errorf("%s could not be converted to JSON: %v\n%s", path, err, string(content)) } - if err := testapi.Default.Codec().DecodeInto(json, expectedType); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), json, expectedType); err != nil { t.Errorf("%s did not decode correctly: %v\n%s", path, err, string(content)) continue } if errors := validateObject(expectedType); len(errors) > 0 { t.Errorf("%s did not validate correctly: %v", path, errors) } - _, err = testapi.Default.Codec().Encode(expectedType) + _, err = runtime.Encode(testapi.Default.Codec(), expectedType) if err != nil { t.Errorf("Could not encode object: %v", err) continue diff --git a/examples/https-nginx/make_secret.go b/examples/https-nginx/make_secret.go index ebefa958664..ae3947fcaac 100644 --- a/examples/https-nginx/make_secret.go +++ b/examples/https-nginx/make_secret.go @@ -66,5 +66,5 @@ func main() { "nginx.key": nginxKey, }, } - fmt.Printf(runtime.EncodeOrDie(registered.GroupOrDie(api.GroupName).Codec, secret)) + fmt.Printf(runtime.EncodeOrDie(api.Codecs.LegacyCodec(registered.EnabledVersions()...), secret)) } diff --git a/examples/sharing-clusters/make_secret.go b/examples/sharing-clusters/make_secret.go index 6219948c3e6..20b35b513d8 100644 --- a/examples/sharing-clusters/make_secret.go +++ b/examples/sharing-clusters/make_secret.go @@ -59,5 +59,5 @@ func main() { "config": cfg, }, } - fmt.Printf(runtime.EncodeOrDie(registered.GroupOrDie(api.GroupName).Codec, secret)) + fmt.Printf(runtime.EncodeOrDie(api.Codecs.LegacyCodec(registered.EnabledVersions()...), secret)) } diff --git a/hack/after-build/update-generated-deep-copies.sh b/hack/after-build/update-generated-deep-copies.sh index 83740149bf4..c3089be5e71 100755 --- a/hack/after-build/update-generated-deep-copies.sh +++ b/hack/after-build/update-generated-deep-copies.sh @@ -54,7 +54,7 @@ function generate_deep_copies() { else apiVersions="${ver}" fi - KUBE_API_VERSIONS="${apiVersions}" generate_version "${ver}" + KUBE_API_VERSIONS="${apiVersions:-}" generate_version "${ver}" done } @@ -62,6 +62,6 @@ function generate_deep_copies() { # Currently pkg/api/deep_copy_generated.go is generated by the new go2idl generator. # All others (mentioned above) are still generated by the old reflection-based generator. # TODO: Migrate these to the new generator. -DEFAULT_VERSIONS="v1 authorization/ authorization/v1beta1 extensions/ extensions/v1beta1 componentconfig/ componentconfig/v1alpha1 metrics/ metrics/v1alpha1" +DEFAULT_VERSIONS="v1 authorization/__internal authorization/v1beta1 extensions/__internal extensions/v1beta1 componentconfig/__internal componentconfig/v1alpha1 metrics/__internal metrics/v1alpha1" VERSIONS=${VERSIONS:-$DEFAULT_VERSIONS} generate_deep_copies "$VERSIONS" diff --git a/hack/lib/util.sh b/hack/lib/util.sh index b4ebec1ecf8..5208a954c75 100755 --- a/hack/lib/util.sh +++ b/hack/lib/util.sh @@ -282,7 +282,7 @@ kube::util::group-version-to-pkg-path() { # moving the results to pkg/apis/api. case "${group_version}" in # both group and version are "", this occurs when we generate deep copies for internal objects of the legacy v1 API. - /) + __internal) echo "api" ;; v1) @@ -292,7 +292,7 @@ kube::util::group-version-to-pkg-path() { echo "api/unversioned" ;; *) - echo "apis/${group_version}" + echo "apis/${group_version%__internal}" ;; esac } diff --git a/pkg/api/conversion.go b/pkg/api/conversion.go index 46e33a8c183..f6c95d0493f 100644 --- a/pkg/api/conversion.go +++ b/pkg/api/conversion.go @@ -22,14 +22,9 @@ import ( "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" - "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/intstr" ) -// Codec is the identity codec for this package - it can only convert itself -// to itself. -var Codec = runtime.CodecFor(Scheme, unversioned.GroupVersion{}) - func init() { Scheme.AddDefaultingFuncs( func(obj *ListOptions) { diff --git a/pkg/api/conversion_test.go b/pkg/api/conversion_test.go index 5ac3fac91d1..1af454ef755 100644 --- a/pkg/api/conversion_test.go +++ b/pkg/api/conversion_test.go @@ -22,6 +22,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/runtime" ) func BenchmarkPodConversion(b *testing.B) { @@ -30,7 +31,7 @@ func BenchmarkPodConversion(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var pod api.Pod - if err := api.Scheme.DecodeInto(data, &pod); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &pod); err != nil { b.Fatalf("Unexpected error decoding pod: %v", err) } @@ -41,7 +42,7 @@ func BenchmarkPodConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.GroupVersion().Group].String()) + obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) if err != nil { b.Fatalf("Conversion error: %v", err) } @@ -58,7 +59,7 @@ func BenchmarkNodeConversion(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var node api.Node - if err := api.Scheme.DecodeInto(data, &node); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &node); err != nil { b.Fatalf("Unexpected error decoding node: %v", err) } @@ -69,7 +70,7 @@ func BenchmarkNodeConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.GroupVersion().Group].String()) + obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) if err != nil { b.Fatalf("Conversion error: %v", err) } @@ -86,7 +87,7 @@ func BenchmarkReplicationControllerConversion(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var replicationController api.ReplicationController - if err := api.Scheme.DecodeInto(data, &replicationController); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &replicationController); err != nil { b.Fatalf("Unexpected error decoding node: %v", err) } @@ -97,7 +98,7 @@ func BenchmarkReplicationControllerConversion(b *testing.B) { if err != nil { b.Fatalf("Conversion error: %v", err) } - obj, err := scheme.ConvertToVersion(versionedObj, scheme.InternalVersions[testapi.Default.GroupVersion().Group].String()) + obj, err := scheme.ConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion().String()) if err != nil { b.Fatalf("Conversion error: %v", err) } diff --git a/pkg/api/copy_test.go b/pkg/api/copy_test.go index 94575c8853a..194b5318a42 100644 --- a/pkg/api/copy_test.go +++ b/pkg/api/copy_test.go @@ -54,7 +54,7 @@ func doDeepCopyTest(t *testing.T, kind unversioned.GroupVersionKind, f *fuzz.Fuz } if !reflect.DeepEqual(item, itemCopy) { - t.Errorf("\nexpected: %#v\n\ngot: %#v\n\ndiff: %v", item, itemCopy, util.ObjectDiff(item, itemCopy)) + t.Errorf("\nexpected: %#v\n\ngot: %#v\n\ndiff: %v", item, itemCopy, util.ObjectGoPrintSideBySide(item, itemCopy)) } } diff --git a/pkg/api/deep_copy_generated.go b/pkg/api/deep_copy_generated.go index c3d8273e0fd..4bd65710dd3 100644 --- a/pkg/api/deep_copy_generated.go +++ b/pkg/api/deep_copy_generated.go @@ -2778,6 +2778,11 @@ func deepCopy_runtime_RawExtension(in runtime.RawExtension, out *runtime.RawExte } else { out.RawJSON = nil } + if newVal, err := c.DeepCopy(in.Object); err != nil { + return err + } else { + out.Object = newVal.(runtime.Object) + } return nil } diff --git a/pkg/api/deep_copy_test.go b/pkg/api/deep_copy_test.go index 606e3ab080d..a251623a054 100644 --- a/pkg/api/deep_copy_test.go +++ b/pkg/api/deep_copy_test.go @@ -21,6 +21,8 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/runtime" ) func BenchmarkPodCopy(b *testing.B) { @@ -29,7 +31,7 @@ func BenchmarkPodCopy(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var pod api.Pod - if err := api.Scheme.DecodeInto(data, &pod); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &pod); err != nil { b.Fatalf("Unexpected error decoding pod: %v", err) } @@ -52,7 +54,7 @@ func BenchmarkNodeCopy(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var node api.Node - if err := api.Scheme.DecodeInto(data, &node); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &node); err != nil { b.Fatalf("Unexpected error decoding node: %v", err) } @@ -75,7 +77,7 @@ func BenchmarkReplicationControllerCopy(b *testing.B) { b.Fatalf("Unexpected error while reading file: %v", err) } var replicationController api.ReplicationController - if err := api.Scheme.DecodeInto(data, &replicationController); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &replicationController); err != nil { b.Fatalf("Unexpected error decoding node: %v", err) } diff --git a/pkg/api/install/install.go b/pkg/api/install/install.go index 1a660f45d06..14cf5551926 100644 --- a/pkg/api/install/install.go +++ b/pkg/api/install/install.go @@ -24,14 +24,13 @@ import ( "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apimachinery" - "k8s.io/kubernetes/pkg/apimachinery/registered" - "k8s.io/kubernetes/pkg/util/sets" - "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/apimachinery" + "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/sets" ) const importPrefix = "k8s.io/kubernetes/pkg/api" @@ -75,7 +74,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -127,7 +125,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/api/install/install_test.go b/pkg/api/install/install_test.go index ad051eb218a..0b161535023 100644 --- a/pkg/api/install/install_test.go +++ b/pkg/api/install/install_test.go @@ -18,11 +18,13 @@ package install import ( "encoding/json" + "reflect" "testing" internal "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/runtime" ) func TestResourceVersioner(t *testing.T) { @@ -49,7 +51,7 @@ func TestCodec(t *testing.T) { pod := internal.Pod{} // We do want to use package registered rather than testapi here, because we // want to test if the package install and package registered work as expected. - data, err := registered.GroupOrDie(internal.GroupName).Codec.Encode(&pod) + data, err := runtime.Encode(internal.Codecs.LegacyCodec(registered.GroupOrDie(internal.GroupName).GroupVersion), &pod) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -100,8 +102,8 @@ func TestRESTMapper(t *testing.T) { } interfaces, _ := registered.GroupOrDie(internal.GroupName).InterfacesFor(version) - if mapping.Codec != interfaces.Codec { - t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) + if mapping.ObjectConvertor != interfaces.ObjectConvertor { + t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) } rc := &internal.ReplicationController{ObjectMeta: internal.ObjectMeta{Name: "foo"}} @@ -114,3 +116,14 @@ func TestRESTMapper(t *testing.T) { } } } + +func TestUnversioned(t *testing.T) { + for _, obj := range []runtime.Object{ + &unversioned.Status{}, + &unversioned.ExportOptions{}, + } { + if unversioned, ok := internal.Scheme.IsUnversioned(obj); !unversioned || !ok { + t.Errorf("%v is expected to be unversioned", reflect.TypeOf(obj)) + } + } +} diff --git a/pkg/api/meta/help.go b/pkg/api/meta/help.go index 3ddcfef87e6..3812f240fb8 100644 --- a/pkg/api/meta/help.go +++ b/pkg/api/meta/help.go @@ -120,7 +120,7 @@ func SetList(list runtime.Object, objects []runtime.Object) error { } else if src.Type().ConvertibleTo(dest.Type()) { dest.Set(src.Convert(dest.Type())) } else { - return fmt.Errorf("item[%d]: Type mismatch: Expected %v, got %v", i, dest.Type(), src.Type()) + return fmt.Errorf("item[%d]: can't assign or convert %v into %v", i, src.Type(), dest.Type()) } } items.Set(slice) diff --git a/pkg/api/meta/interfaces.go b/pkg/api/meta/interfaces.go index 8f14a3fab50..cc81aafa137 100644 --- a/pkg/api/meta/interfaces.go +++ b/pkg/api/meta/interfaces.go @@ -24,7 +24,6 @@ import ( // VersionInterfaces contains the interfaces one should use for dealing with types of a particular version. type VersionInterfaces struct { - runtime.Codec runtime.ObjectConvertor MetadataAccessor } @@ -142,7 +141,6 @@ type RESTMapping struct { // Scope contains the information needed to deal with REST Resources that are in a resource hierarchy Scope RESTScope - runtime.Codec runtime.ObjectConvertor MetadataAccessor } diff --git a/pkg/api/meta/meta_test.go b/pkg/api/meta/meta_test.go index 9d6c7abd62f..5b6c35230e4 100644 --- a/pkg/api/meta/meta_test.go +++ b/pkg/api/meta/meta_test.go @@ -73,7 +73,7 @@ func TestAPIObjectMeta(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if e, a := "/a", typeAccessor.GetAPIVersion(); e != a { + if e, a := "a", typeAccessor.GetAPIVersion(); e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := "b", typeAccessor.GetKind(); e != a { @@ -102,7 +102,7 @@ func TestAPIObjectMeta(t *testing.T) { if e, a := types.UID("other"), j.UID; e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "/c", j.APIVersion; e != a { + if e, a := "c", j.APIVersion; e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := "d", j.Kind; e != a { @@ -117,7 +117,7 @@ func TestAPIObjectMeta(t *testing.T) { typeAccessor.SetAPIVersion("d") typeAccessor.SetKind("e") - if e, a := "/d", j.APIVersion; e != a { + if e, a := "d", j.APIVersion; e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := "e", j.Kind; e != a { @@ -308,7 +308,7 @@ func TestGenericTypeMetaAccessor(t *testing.T) { if err != nil { t.Errorf("unexpected error: %v", err) } - if e, a := "/a", apiVersion; e != a { + if e, a := "a", apiVersion; e != a { t.Errorf("expected %v, got %v", e, a) } kind, err := accessor.Kind(j) @@ -392,7 +392,7 @@ func TestGenericTypeMetaAccessor(t *testing.T) { if e, a := "other", j.TypeMeta.UID; e != a { t.Errorf("expected %v, got %v", e, a) } - if e, a := "/c", j.TypeMeta.APIVersion; e != a { + if e, a := "c", j.TypeMeta.APIVersion; e != a { t.Errorf("expected %v, got %v", e, a) } if e, a := "d", j.TypeMeta.Kind; e != a { @@ -757,7 +757,7 @@ func BenchmarkAccessorSetReflection(b *testing.B) { Name: "foo", GenerateName: "prefix", UID: "uid", - APIVersion: "/a", + APIVersion: "a", Kind: "b", ResourceVersion: "1", SelfLink: "some/place/only/we/know", diff --git a/pkg/api/meta/restmapper.go b/pkg/api/meta/restmapper.go index 449f519afca..f1a4d4113bf 100644 --- a/pkg/api/meta/restmapper.go +++ b/pkg/api/meta/restmapper.go @@ -23,6 +23,7 @@ import ( "strings" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/sets" ) @@ -84,7 +85,7 @@ type DefaultRESTMapper struct { var _ RESTMapper = &DefaultRESTMapper{} -// VersionInterfacesFunc returns the appropriate codec, typer, and metadata accessor for a +// VersionInterfacesFunc returns the appropriate typer, and metadata accessor for a // given api version, or an error if no such api version exists. type VersionInterfacesFunc func(version unversioned.GroupVersion) (*VersionInterfaces, error) @@ -92,7 +93,7 @@ type VersionInterfacesFunc func(version unversioned.GroupVersion) (*VersionInter // to a resource name and back based on the objects in a runtime.Scheme // and the Kubernetes API conventions. Takes a group name, a priority list of the versions // to search when an object has no default version (set empty to return an error), -// and a function that retrieves the correct codec and metadata for a given version. +// and a function that retrieves the correct metadata for a given version. func NewDefaultRESTMapper(defaultGroupVersions []unversioned.GroupVersion, f VersionInterfacesFunc) *DefaultRESTMapper { resourceToKind := make(map[unversioned.GroupVersionResource]unversioned.GroupVersionKind) kindToPluralResource := make(map[unversioned.GroupVersionKind]unversioned.GroupVersionResource) @@ -251,6 +252,9 @@ func (m *DefaultRESTMapper) ResourceFor(resource unversioned.GroupVersionResourc func (m *DefaultRESTMapper) KindsFor(input unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) { resource := input.GroupVersion().WithResource(strings.ToLower(input.Resource)) + if resource.Version == runtime.APIVersionInternal { + resource.Version = "" + } hasResource := len(resource.Resource) > 0 hasGroup := len(resource.Group) > 0 @@ -412,7 +416,7 @@ func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...st var gvk *unversioned.GroupVersionKind hadVersion := false for _, version := range versions { - if len(version) == 0 { + if len(version) == 0 || version == runtime.APIVersionInternal { continue } @@ -472,7 +476,6 @@ func (m *DefaultRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...st GroupVersionKind: *gvk, Scope: scope, - Codec: interfaces.Codec, ObjectConvertor: interfaces.ObjectConvertor, MetadataAccessor: interfaces.MetadataAccessor, } diff --git a/pkg/api/meta/restmapper_test.go b/pkg/api/meta/restmapper_test.go index 603848d5c87..17525df502e 100644 --- a/pkg/api/meta/restmapper_test.go +++ b/pkg/api/meta/restmapper_test.go @@ -18,8 +18,6 @@ package meta import ( "errors" - "io" - "net/url" "reflect" "strings" "testing" @@ -28,38 +26,6 @@ import ( "k8s.io/kubernetes/pkg/runtime" ) -type fakeCodec struct{} - -var _ runtime.Decoder = fakeCodec{} - -func (fakeCodec) Encode(runtime.Object) ([]byte, error) { - return []byte{}, nil -} - -func (fakeCodec) EncodeToStream(runtime.Object, io.Writer) error { - return nil -} - -func (fakeCodec) Decode([]byte) (runtime.Object, error) { - return nil, nil -} - -func (fakeCodec) DecodeToVersion([]byte, unversioned.GroupVersion) (runtime.Object, error) { - return nil, nil -} - -func (fakeCodec) DecodeInto([]byte, runtime.Object) error { - return nil -} - -func (fakeCodec) DecodeIntoWithSpecifiedVersionKind([]byte, runtime.Object, unversioned.GroupVersionKind) error { - return nil -} - -func (fakeCodec) DecodeParametersInto(parameters url.Values, obj runtime.Object) error { - return nil -} - type fakeConvertor struct{} func (fakeConvertor) Convert(in, out interface{}) error { @@ -74,12 +40,11 @@ func (fakeConvertor) ConvertFieldLabel(version, kind, label, value string) (stri return label, value, nil } -var validCodec = fakeCodec{} var validAccessor = resourceAccessor{} var validConvertor = fakeConvertor{} func fakeInterfaces(version unversioned.GroupVersion) (*VersionInterfaces, error) { - return &VersionInterfaces{Codec: validCodec, ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, nil + return &VersionInterfaces{ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, nil } var unmatchedErr = errors.New("no version") @@ -512,7 +477,7 @@ func TestRESTMapperRESTMapping(t *testing.T) { t.Errorf("%d: unexpected resource: %#v", i, mapping) } - if mapping.Codec == nil || mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil { + if mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil { t.Errorf("%d: missing codec and accessor: %#v", i, mapping) } diff --git a/pkg/api/register.go b/pkg/api/register.go index c1e9e9157aa..4aff2256be0 100644 --- a/pkg/api/register.go +++ b/pkg/api/register.go @@ -20,16 +20,27 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" ) // Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered. var Scheme = runtime.NewScheme() +// Codecs provides access to encoding and decoding for the scheme +var Codecs = serializer.NewCodecFactory(Scheme) + // GroupName is the group name use in this package const GroupName = "" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +// Unversiond is group version for unversioned API objects +// TODO: this should be v1 probably +var Unversioned = unversioned.GroupVersion{Group: "", Version: "v1"} + +// ParameterCodec handles versioning of objects that are converted to query parameters. +var ParameterCodec = runtime.NewParameterCodec(Scheme) // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { @@ -42,6 +53,9 @@ func Resource(resource string) unversioned.GroupResource { } func AddToScheme(scheme *runtime.Scheme) { + if err := Scheme.AddIgnoredConversionType(&unversioned.TypeMeta{}, &unversioned.TypeMeta{}); err != nil { + panic(err) + } scheme.AddKnownTypes(SchemeGroupVersion, &Pod{}, &PodList{}, @@ -86,14 +100,15 @@ func AddToScheme(scheme *runtime.Scheme) { &RangeAllocation{}, ) - // Add the Unversioned types to scheme. - // TODO this should not be done here - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.ExportOptions{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.Status{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIVersions{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIGroupList{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIGroup{}) - scheme.AddKnownTypes(SchemeGroupVersion, &unversioned.APIResourceList{}) + // Register Unversioned types under their own special group + Scheme.AddUnversionedTypes(Unversioned, + &unversioned.ExportOptions{}, + &unversioned.Status{}, + &unversioned.APIVersions{}, + &unversioned.APIGroupList{}, + &unversioned.APIGroup{}, + &unversioned.APIResourceList{}, + ) } func (obj *Pod) GetObjectMeta() meta.Object { return &obj.ObjectMeta } diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index bcae9883c95..0f69b8be81d 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -61,6 +61,8 @@ func fuzzInternalObject(t *testing.T, forVersion unversioned.GroupVersion, item } func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { + t.Logf("codec: %#v", codec) + printer := spew.ConfigState{DisableMethods: true} name := reflect.TypeOf(item).Elem().Name() @@ -81,8 +83,7 @@ func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { } obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) - err = runtime.DecodeInto(codec, data, obj3) - if err != nil { + if err := runtime.DecodeInto(codec, data, obj3); err != nil { t.Errorf("2: %v: %v", name, err) return } @@ -122,7 +123,7 @@ func TestSpecificKind(t *testing.T) { // api.Scheme.Log(t) // defer api.Scheme.Log(nil) - kind := "Pod" + kind := "List" for i := 0; i < *fuzzIters; i++ { doRoundTripTest(kind, t) if t.Failed() { @@ -180,7 +181,7 @@ func doRoundTripTest(kind string, t *testing.T) { roundTripSame(t, item, nonRoundTrippableTypesByVersion[kind]...) } if !nonInternalRoundTrippableTypes.Has(kind) { - roundTrip(t, api.Codec, fuzzInternalObject(t, testapi.Default.InternalGroupVersion(), item, rand.Int63())) + roundTrip(t, testapi.Default.Codec(), fuzzInternalObject(t, testapi.Default.InternalGroupVersion(), item, rand.Int63())) } } @@ -200,8 +201,8 @@ func TestEncode_Ptr(t *testing.T) { }, } obj := runtime.Object(pod) - data, err := testapi.Default.Codec().Encode(obj) - obj2, err2 := testapi.Default.Codec().Decode(data) + data, err := runtime.Encode(testapi.Default.Codec(), obj) + obj2, err2 := runtime.Decode(testapi.Default.Codec(), data) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } @@ -216,11 +217,11 @@ func TestEncode_Ptr(t *testing.T) { func TestBadJSONRejection(t *testing.T) { badJSONMissingKind := []byte(`{ }`) - if _, err := testapi.Default.Codec().Decode(badJSONMissingKind); err == nil { + if _, err := runtime.Decode(testapi.Default.Codec(), badJSONMissingKind); err == nil { t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) } badJSONUnknownType := []byte(`{"kind": "bar"}`) - if _, err1 := testapi.Default.Codec().Decode(badJSONUnknownType); err1 == nil { + if _, err1 := runtime.Decode(testapi.Default.Codec(), badJSONUnknownType); err1 == nil { t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) } /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) @@ -240,14 +241,14 @@ func TestUnversionedTypes(t *testing.T) { for _, obj := range testcases { // Make sure the unversioned codec can encode - unversionedJSON, err := api.Codec.Encode(obj) + unversionedJSON, err := runtime.Encode(testapi.Default.Codec(), obj) if err != nil { t.Errorf("%v: unexpected error: %v", obj, err) continue } // Make sure the versioned codec under test can decode - versionDecodedObject, err := testapi.Default.Codec().Decode(unversionedJSON) + versionDecodedObject, err := runtime.Decode(testapi.Default.Codec(), unversionedJSON) if err != nil { t.Errorf("%v: unexpected error: %v", obj, err) continue @@ -278,7 +279,7 @@ func BenchmarkEncodeCodec(b *testing.B) { width := len(items) b.ResetTimer() for i := 0; i < b.N; i++ { - if _, err := testapi.Default.Codec().Encode(&items[i%width]); err != nil { + if _, err := runtime.Encode(testapi.Default.Codec(), &items[i%width]); err != nil { b.Fatal(err) } } @@ -320,7 +321,7 @@ func BenchmarkDecodeCodec(b *testing.B) { b.StopTimer() } -func BenchmarkDecodeIntoCodec(b *testing.B) { +func BenchmarkDecodeIntoExternalCodec(b *testing.B) { codec := testapi.Default.Codec() items := benchmarkItems() width := len(items) @@ -343,6 +344,29 @@ func BenchmarkDecodeIntoCodec(b *testing.B) { b.StopTimer() } +func BenchmarkDecodeIntoInternalCodec(b *testing.B) { + codec := testapi.Default.Codec() + items := benchmarkItems() + width := len(items) + encoded := make([][]byte, width) + for i := range items { + data, err := runtime.Encode(codec, &items[i]) + if err != nil { + b.Fatal(err) + } + encoded[i] = data + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + obj := api.Pod{} + if err := runtime.DecodeInto(codec, encoded[i%width], &obj); err != nil { + b.Fatal(err) + } + } + b.StopTimer() +} + // BenchmarkDecodeJSON provides a baseline for regular JSON decode performance func BenchmarkDecodeIntoJSON(b *testing.B) { codec := testapi.Default.Codec() diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index 09a5d1b4a98..eb5cd8aa163 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -47,7 +47,7 @@ type TestGroup struct { func init() { kubeTestAPI := os.Getenv("KUBE_TEST_API") - if kubeTestAPI != "" { + if len(kubeTestAPI) != 0 { testGroupVersions := strings.Split(kubeTestAPI, ",") for _, gvString := range testGroupVersions { groupVersion, err := unversioned.ParseGroupVersion(gvString) @@ -57,7 +57,7 @@ func init() { Groups[groupVersion.Group] = TestGroup{ externalGroupVersion: groupVersion, - internalGroupVersion: unversioned.GroupVersion{Group: groupVersion.Group}, + internalGroupVersion: unversioned.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}, } } } @@ -93,12 +93,7 @@ func (g TestGroup) InternalGroupVersion() unversioned.GroupVersion { // Codec returns the codec for the API version to test against, as set by the // KUBE_TEST_API env var. func (g TestGroup) Codec() runtime.Codec { - // TODO: caesarxuchao: Restructure the body once we have a central `registered`. - interfaces, err := registered.GroupOrDie(g.externalGroupVersion.Group).InterfacesFor(g.externalGroupVersion) - if err != nil { - panic(err) - } - return interfaces.Codec + return api.Codecs.LegacyCodec(g.externalGroupVersion) } // Converter returns the api.Scheme for the API version to test against, as set by the @@ -199,7 +194,11 @@ func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) { } // Codec used for unversioned types if api.Scheme.Recognizes(kind) { - return api.Codec, nil + serializer, ok := api.Codecs.SerializerForFileExtension("json") + if !ok { + return nil, fmt.Errorf("no serializer registered for json") + } + return serializer, nil } return nil, fmt.Errorf("unexpected kind: %v", kind) } diff --git a/pkg/api/testapi/testapi_test.go b/pkg/api/testapi/testapi_test.go index b90ea579ab6..7b8bb02ba36 100644 --- a/pkg/api/testapi/testapi_test.go +++ b/pkg/api/testapi/testapi_test.go @@ -17,7 +17,12 @@ limitations under the License. package testapi import ( + "encoding/json" + "reflect" "testing" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" ) // TODO these tests don't add much value for testing things that have groups @@ -61,3 +66,61 @@ func TestResourcePath(t *testing.T) { } } } + +var status = &unversioned.Status{ + Status: unversioned.StatusFailure, + Code: 200, + Reason: unversioned.StatusReasonUnknown, + Message: "", +} + +func TestV1EncodeDecodeStatus(t *testing.T) { + v1Codec := Default.Codec() + + encoded, err := runtime.Encode(v1Codec, status) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + typeMeta := unversioned.TypeMeta{} + if err := json.Unmarshal(encoded, &typeMeta); err != nil { + t.Errorf("unexpected error: %v", err) + } + if typeMeta.Kind != "Status" { + t.Errorf("Kind is not set to \"Status\". Got %v", string(encoded)) + } + if typeMeta.APIVersion != "v1" { + t.Errorf("APIVersion is not set to \"v1\". Got %v", string(encoded)) + } + decoded, err := runtime.Decode(v1Codec, encoded) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !reflect.DeepEqual(status, decoded) { + t.Errorf("expected: %#v, got: %#v", status, decoded) + } +} + +func TestExperimentalEncodeDecodeStatus(t *testing.T) { + extensionCodec := Extensions.Codec() + encoded, err := runtime.Encode(extensionCodec, status) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + typeMeta := unversioned.TypeMeta{} + if err := json.Unmarshal(encoded, &typeMeta); err != nil { + t.Errorf("unexpected error: %v", err) + } + if typeMeta.Kind != "Status" { + t.Errorf("Kind is not set to \"Status\". Got %s", encoded) + } + if typeMeta.APIVersion != "v1" { + t.Errorf("APIVersion is not set to \"\". Got %s", encoded) + } + decoded, err := runtime.Decode(extensionCodec, encoded) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !reflect.DeepEqual(status, decoded) { + t.Errorf("expected: %v, got: %v", status, decoded) + } +} diff --git a/pkg/api/testing/compat/compatibility_tester.go b/pkg/api/testing/compat/compatibility_tester.go index 48174fc3d82..82f165a339d 100644 --- a/pkg/api/testing/compat/compatibility_tester.go +++ b/pkg/api/testing/compat/compatibility_tester.go @@ -48,7 +48,7 @@ func TestCompatibility( ) { // Decode - codec := runtime.CodecFor(api.Scheme, version) + codec := api.Codecs.LegacyCodec(version) obj, err := runtime.Decode(codec, input) if err != nil { t.Fatalf("Unexpected error: %v", err) diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index c34f07effad..30a2ed18746 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -59,9 +59,6 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) func(q *resource.Quantity, c fuzz.Continue) { *q = *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent) }, - func(j *runtime.PluginBase, c fuzz.Continue) { - // Do nothing; this struct has only a Kind field and it must stay blank in memory. - }, func(j *runtime.TypeMeta, c fuzz.Continue) { // We have to customize the randomization of TypeMetas because their // APIVersion and Kind must remain blank in memory. @@ -177,10 +174,8 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) // TODO: uncomment when round trip starts from a versioned object if true { //c.RandBool() { *j = &runtime.Unknown{ - // apiVersion has rules now. Since it includes / and only `v1` can be bare, - // then this must choose a valid format to deserialize - TypeMeta: runtime.TypeMeta{Kind: "Something", APIVersion: "unknown.group/unknown"}, - RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), + // We do not set TypeMeta here because it is not carried through a round trip + RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`), } } else { types := []runtime.Object{&api.Pod{}, &api.ReplicationController{}} diff --git a/pkg/api/unversioned/group_version.go b/pkg/api/unversioned/group_version.go index a77ea9ef7ce..5b684915afc 100644 --- a/pkg/api/unversioned/group_version.go +++ b/pkg/api/unversioned/group_version.go @@ -141,11 +141,13 @@ func (gv GroupVersion) String() string { } // special case of "v1" for backward compatibility - if gv.Group == "" && gv.Version == "v1" { + if len(gv.Group) == 0 && gv.Version == "v1" { return gv.Version - } else { + } + if len(gv.Group) > 0 { return gv.Group + "/" + gv.Version } + return gv.Version } // ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error @@ -163,6 +165,8 @@ func ParseGroupVersion(gv string) (GroupVersion, error) { switch { case len(s) == 1 && gv == "v1": return GroupVersion{"", "v1"}, nil + case len(s) == 1: + return GroupVersion{"", s[0]}, nil case len(s) == 2: return GroupVersion{s[0], s[1]}, nil default: diff --git a/pkg/api/v1/conversion_generated.go b/pkg/api/v1/conversion_generated.go index c3d6674c82f..b7f2d780287 100644 --- a/pkg/api/v1/conversion_generated.go +++ b/pkg/api/v1/conversion_generated.go @@ -25,6 +25,7 @@ import ( resource "k8s.io/kubernetes/pkg/api/resource" unversioned "k8s.io/kubernetes/pkg/api/unversioned" conversion "k8s.io/kubernetes/pkg/conversion" + runtime "k8s.io/kubernetes/pkg/runtime" ) func autoConvert_api_AWSElasticBlockStoreVolumeSource_To_v1_AWSElasticBlockStoreVolumeSource(in *api.AWSElasticBlockStoreVolumeSource, out *AWSElasticBlockStoreVolumeSource, s conversion.Scope) error { @@ -46,9 +47,6 @@ func autoConvert_api_Binding_To_v1_Binding(in *api.Binding, out *Binding, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Binding))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -153,9 +151,6 @@ func autoConvert_api_ComponentStatus_To_v1_ComponentStatus(in *api.ComponentStat if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ComponentStatus))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -180,9 +175,6 @@ func autoConvert_api_ComponentStatusList_To_v1_ComponentStatusList(in *api.Compo if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ComponentStatusList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -484,9 +476,6 @@ func autoConvert_api_DeleteOptions_To_v1_DeleteOptions(in *api.DeleteOptions, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.DeleteOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if in.GracePeriodSeconds != nil { out.GracePeriodSeconds = new(int64) *out.GracePeriodSeconds = *in.GracePeriodSeconds @@ -628,9 +617,6 @@ func autoConvert_api_Endpoints_To_v1_Endpoints(in *api.Endpoints, out *Endpoints if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Endpoints))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -655,9 +641,6 @@ func autoConvert_api_EndpointsList_To_v1_EndpointsList(in *api.EndpointsList, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.EndpointsList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -742,9 +725,6 @@ func autoConvert_api_Event_To_v1_Event(in *api.Event, out *Event, s conversion.S if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Event))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -775,9 +755,6 @@ func autoConvert_api_EventList_To_v1_EventList(in *api.EventList, out *EventList if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.EventList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1060,9 +1037,6 @@ func autoConvert_api_LimitRange_To_v1_LimitRange(in *api.LimitRange, out *LimitR if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.LimitRange))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1152,9 +1126,6 @@ func autoConvert_api_LimitRangeList_To_v1_LimitRangeList(in *api.LimitRangeList, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.LimitRangeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1200,14 +1171,18 @@ func autoConvert_api_List_To_v1_List(in *api.List, out *List, s conversion.Scope if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.List))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } - if err := s.Convert(&in.Items, &out.Items, 0); err != nil { - return err + if in.Items != nil { + out.Items = make([]runtime.RawExtension, len(in.Items)) + for i := range in.Items { + if err := s.Convert(&in.Items[i], &out.Items[i], 0); err != nil { + return err + } + } + } else { + out.Items = nil } return nil } @@ -1220,9 +1195,6 @@ func autoConvert_api_ListOptions_To_v1_ListOptions(in *api.ListOptions, out *Lis if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ListOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_labels_Selector_To_string(&in.LabelSelector, &out.LabelSelector, s); err != nil { return err } @@ -1308,9 +1280,6 @@ func autoConvert_api_Namespace_To_v1_Namespace(in *api.Namespace, out *Namespace if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Namespace))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1331,9 +1300,6 @@ func autoConvert_api_NamespaceList_To_v1_NamespaceList(in *api.NamespaceList, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.NamespaceList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1389,9 +1355,6 @@ func autoConvert_api_Node_To_v1_Node(in *api.Node, out *Node, s conversion.Scope if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Node))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1460,9 +1423,6 @@ func autoConvert_api_NodeList_To_v1_NodeList(in *api.NodeList, out *NodeList, s if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.NodeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1676,9 +1636,6 @@ func autoConvert_api_PersistentVolume_To_v1_PersistentVolume(in *api.PersistentV if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PersistentVolume))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1699,9 +1656,6 @@ func autoConvert_api_PersistentVolumeClaim_To_v1_PersistentVolumeClaim(in *api.P if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PersistentVolumeClaim))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -1722,9 +1676,6 @@ func autoConvert_api_PersistentVolumeClaimList_To_v1_PersistentVolumeClaimList(i if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PersistentVolumeClaimList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -1817,9 +1768,6 @@ func autoConvert_api_PersistentVolumeList_To_v1_PersistentVolumeList(in *api.Per if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PersistentVolumeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2021,9 +1969,6 @@ func autoConvert_api_Pod_To_v1_Pod(in *api.Pod, out *Pod, s conversion.Scope) er if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Pod))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2040,9 +1985,6 @@ func autoConvert_api_PodAttachOptions_To_v1_PodAttachOptions(in *api.PodAttachOp if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodAttachOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Stdin = in.Stdin out.Stdout = in.Stdout out.Stderr = in.Stderr @@ -2080,9 +2022,6 @@ func autoConvert_api_PodExecOptions_To_v1_PodExecOptions(in *api.PodExecOptions, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodExecOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Stdin = in.Stdin out.Stdout = in.Stdout out.Stderr = in.Stderr @@ -2107,9 +2046,6 @@ func autoConvert_api_PodList_To_v1_PodList(in *api.PodList, out *PodList, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2134,9 +2070,6 @@ func autoConvert_api_PodLogOptions_To_v1_PodLogOptions(in *api.PodLogOptions, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodLogOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Container = in.Container out.Follow = in.Follow out.Previous = in.Previous @@ -2179,9 +2112,6 @@ func autoConvert_api_PodProxyOptions_To_v1_PodProxyOptions(in *api.PodProxyOptio if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodProxyOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Path = in.Path return nil } @@ -2308,9 +2238,6 @@ func autoConvert_api_PodStatusResult_To_v1_PodStatusResult(in *api.PodStatusResu if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodStatusResult))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2328,9 +2255,6 @@ func autoConvert_api_PodTemplate_To_v1_PodTemplate(in *api.PodTemplate, out *Pod if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodTemplate))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2348,9 +2272,6 @@ func autoConvert_api_PodTemplateList_To_v1_PodTemplateList(in *api.PodTemplateLi if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.PodTemplateList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2445,9 +2366,6 @@ func autoConvert_api_RangeAllocation_To_v1_RangeAllocation(in *api.RangeAllocati if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.RangeAllocation))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2466,9 +2384,6 @@ func autoConvert_api_ReplicationController_To_v1_ReplicationController(in *api.R if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ReplicationController))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2489,9 +2404,6 @@ func autoConvert_api_ReplicationControllerList_To_v1_ReplicationControllerList(i if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ReplicationControllerList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2556,9 +2468,6 @@ func autoConvert_api_ResourceQuota_To_v1_ResourceQuota(in *api.ResourceQuota, ou if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ResourceQuota))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2579,9 +2488,6 @@ func autoConvert_api_ResourceQuotaList_To_v1_ResourceQuotaList(in *api.ResourceQ if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ResourceQuotaList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2714,9 +2620,6 @@ func autoConvert_api_Secret_To_v1_Secret(in *api.Secret, out *Secret, s conversi if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Secret))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2759,9 +2662,6 @@ func autoConvert_api_SecretList_To_v1_SecretList(in *api.SecretList, out *Secret if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.SecretList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2845,9 +2745,6 @@ func autoConvert_api_SerializedReference_To_v1_SerializedReference(in *api.Seria if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.SerializedReference))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectReference_To_v1_ObjectReference(&in.Reference, &out.Reference, s); err != nil { return err } @@ -2862,9 +2759,6 @@ func autoConvert_api_Service_To_v1_Service(in *api.Service, out *Service, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.Service))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2885,9 +2779,6 @@ func autoConvert_api_ServiceAccount_To_v1_ServiceAccount(in *api.ServiceAccount, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ServiceAccount))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -2922,9 +2813,6 @@ func autoConvert_api_ServiceAccountList_To_v1_ServiceAccountList(in *api.Service if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ServiceAccountList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -2949,9 +2837,6 @@ func autoConvert_api_ServiceList_To_v1_ServiceList(in *api.ServiceList, out *Ser if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*api.ServiceList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -3252,9 +3137,6 @@ func autoConvert_unversioned_ExportOptions_To_v1_ExportOptions(in *unversioned.E if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*unversioned.ExportOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Export = in.Export out.Exact = in.Exact return nil @@ -3283,9 +3165,6 @@ func autoConvert_v1_Binding_To_api_Binding(in *Binding, out *api.Binding, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Binding))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -3390,9 +3269,6 @@ func autoConvert_v1_ComponentStatus_To_api_ComponentStatus(in *ComponentStatus, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ComponentStatus))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -3417,9 +3293,6 @@ func autoConvert_v1_ComponentStatusList_To_api_ComponentStatusList(in *Component if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ComponentStatusList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -3721,9 +3594,6 @@ func autoConvert_v1_DeleteOptions_To_api_DeleteOptions(in *DeleteOptions, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*DeleteOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if in.GracePeriodSeconds != nil { out.GracePeriodSeconds = new(int64) *out.GracePeriodSeconds = *in.GracePeriodSeconds @@ -3865,9 +3735,6 @@ func autoConvert_v1_Endpoints_To_api_Endpoints(in *Endpoints, out *api.Endpoints if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Endpoints))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -3892,9 +3759,6 @@ func autoConvert_v1_EndpointsList_To_api_EndpointsList(in *EndpointsList, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*EndpointsList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -3979,9 +3843,6 @@ func autoConvert_v1_Event_To_api_Event(in *Event, out *api.Event, s conversion.S if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Event))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4012,9 +3873,6 @@ func autoConvert_v1_EventList_To_api_EventList(in *EventList, out *api.EventList if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*EventList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4071,9 +3929,6 @@ func autoConvert_v1_ExportOptions_To_unversioned_ExportOptions(in *ExportOptions if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ExportOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Export = in.Export out.Exact = in.Exact return nil @@ -4313,9 +4168,6 @@ func autoConvert_v1_LimitRange_To_api_LimitRange(in *LimitRange, out *api.LimitR if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*LimitRange))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4360,9 +4212,6 @@ func autoConvert_v1_LimitRangeList_To_api_LimitRangeList(in *LimitRangeList, out if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*LimitRangeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4408,14 +4257,18 @@ func autoConvert_v1_List_To_api_List(in *List, out *api.List, s conversion.Scope if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*List))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } - if err := s.Convert(&in.Items, &out.Items, 0); err != nil { - return err + if in.Items != nil { + out.Items = make([]runtime.Object, len(in.Items)) + for i := range in.Items { + if err := s.Convert(&in.Items[i], &out.Items[i], 0); err != nil { + return err + } + } + } else { + out.Items = nil } return nil } @@ -4428,9 +4281,6 @@ func autoConvert_v1_ListOptions_To_api_ListOptions(in *ListOptions, out *api.Lis if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ListOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_string_To_labels_Selector(&in.LabelSelector, &out.LabelSelector, s); err != nil { return err } @@ -4516,9 +4366,6 @@ func autoConvert_v1_Namespace_To_api_Namespace(in *Namespace, out *api.Namespace if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Namespace))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4539,9 +4386,6 @@ func autoConvert_v1_NamespaceList_To_api_NamespaceList(in *NamespaceList, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*NamespaceList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4597,9 +4441,6 @@ func autoConvert_v1_Node_To_api_Node(in *Node, out *api.Node, s conversion.Scope if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Node))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4668,9 +4509,6 @@ func autoConvert_v1_NodeList_To_api_NodeList(in *NodeList, out *api.NodeList, s if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*NodeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4866,9 +4704,6 @@ func autoConvert_v1_PersistentVolume_To_api_PersistentVolume(in *PersistentVolum if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PersistentVolume))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4889,9 +4724,6 @@ func autoConvert_v1_PersistentVolumeClaim_To_api_PersistentVolumeClaim(in *Persi if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PersistentVolumeClaim))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -4912,9 +4744,6 @@ func autoConvert_v1_PersistentVolumeClaimList_To_api_PersistentVolumeClaimList(i if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PersistentVolumeClaimList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -4998,9 +4827,6 @@ func autoConvert_v1_PersistentVolumeList_To_api_PersistentVolumeList(in *Persist if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PersistentVolumeList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5193,9 +5019,6 @@ func autoConvert_v1_Pod_To_api_Pod(in *Pod, out *api.Pod, s conversion.Scope) er if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Pod))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5212,9 +5035,6 @@ func autoConvert_v1_PodAttachOptions_To_api_PodAttachOptions(in *PodAttachOption if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodAttachOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Stdin = in.Stdin out.Stdout = in.Stdout out.Stderr = in.Stderr @@ -5252,9 +5072,6 @@ func autoConvert_v1_PodExecOptions_To_api_PodExecOptions(in *PodExecOptions, out if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodExecOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Stdin = in.Stdin out.Stdout = in.Stdout out.Stderr = in.Stderr @@ -5279,9 +5096,6 @@ func autoConvert_v1_PodList_To_api_PodList(in *PodList, out *api.PodList, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5306,9 +5120,6 @@ func autoConvert_v1_PodLogOptions_To_api_PodLogOptions(in *PodLogOptions, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodLogOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Container = in.Container out.Follow = in.Follow out.Previous = in.Previous @@ -5351,9 +5162,6 @@ func autoConvert_v1_PodProxyOptions_To_api_PodProxyOptions(in *PodProxyOptions, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodProxyOptions))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } out.Path = in.Path return nil } @@ -5484,9 +5292,6 @@ func autoConvert_v1_PodStatusResult_To_api_PodStatusResult(in *PodStatusResult, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodStatusResult))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5504,9 +5309,6 @@ func autoConvert_v1_PodTemplate_To_api_PodTemplate(in *PodTemplate, out *api.Pod if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodTemplate))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5524,9 +5326,6 @@ func autoConvert_v1_PodTemplateList_To_api_PodTemplateList(in *PodTemplateList, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*PodTemplateList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5621,9 +5420,6 @@ func autoConvert_v1_RangeAllocation_To_api_RangeAllocation(in *RangeAllocation, if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*RangeAllocation))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5642,9 +5438,6 @@ func autoConvert_v1_ReplicationController_To_api_ReplicationController(in *Repli if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ReplicationController))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5665,9 +5458,6 @@ func autoConvert_v1_ReplicationControllerList_To_api_ReplicationControllerList(i if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ReplicationControllerList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5730,9 +5520,6 @@ func autoConvert_v1_ResourceQuota_To_api_ResourceQuota(in *ResourceQuota, out *a if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ResourceQuota))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5753,9 +5540,6 @@ func autoConvert_v1_ResourceQuotaList_To_api_ResourceQuotaList(in *ResourceQuota if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ResourceQuotaList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5843,9 +5627,6 @@ func autoConvert_v1_Secret_To_api_Secret(in *Secret, out *api.Secret, s conversi if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Secret))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -5888,9 +5669,6 @@ func autoConvert_v1_SecretList_To_api_SecretList(in *SecretList, out *api.Secret if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*SecretList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -5974,9 +5752,6 @@ func autoConvert_v1_SerializedReference_To_api_SerializedReference(in *Serialize if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*SerializedReference))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectReference_To_api_ObjectReference(&in.Reference, &out.Reference, s); err != nil { return err } @@ -5991,9 +5766,6 @@ func autoConvert_v1_Service_To_api_Service(in *Service, out *api.Service, s conv if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*Service))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -6014,9 +5786,6 @@ func autoConvert_v1_ServiceAccount_To_api_ServiceAccount(in *ServiceAccount, out if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ServiceAccount))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := Convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { return err } @@ -6051,9 +5820,6 @@ func autoConvert_v1_ServiceAccountList_To_api_ServiceAccountList(in *ServiceAcco if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ServiceAccountList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } @@ -6078,9 +5844,6 @@ func autoConvert_v1_ServiceList_To_api_ServiceList(in *ServiceList, out *api.Ser if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ServiceList))(in) } - if err := api.Convert_unversioned_TypeMeta_To_unversioned_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil { - return err - } if err := api.Convert_unversioned_ListMeta_To_unversioned_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil { return err } diff --git a/pkg/api/v1/deep_copy_generated.go b/pkg/api/v1/deep_copy_generated.go index 2bc06f6daa6..581e006ef94 100644 --- a/pkg/api/v1/deep_copy_generated.go +++ b/pkg/api/v1/deep_copy_generated.go @@ -2471,6 +2471,13 @@ func deepCopy_runtime_RawExtension(in runtime.RawExtension, out *runtime.RawExte } else { out.RawJSON = nil } + if newVal, err := c.DeepCopy(in.Object); err != nil { + return err + } else if newVal == nil { + out.Object = nil + } else { + out.Object = newVal.(runtime.Object) + } return nil } diff --git a/pkg/api/v1/defaults_test.go b/pkg/api/v1/defaults_test.go index 0c2b5b29884..b1a78e758e3 100644 --- a/pkg/api/v1/defaults_test.go +++ b/pkg/api/v1/defaults_test.go @@ -28,12 +28,13 @@ import ( ) func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { - data, err := versioned.Codec.Encode(obj) + codec := api.Codecs.LegacyCodec(versioned.SchemeGroupVersion) + data, err := runtime.Encode(codec, obj) if err != nil { t.Errorf("%v\n %#v", err, obj) return nil } - obj2, err := api.Codec.Decode(data) + obj2, err := runtime.Decode(codec, data) if err != nil { t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) return nil diff --git a/pkg/api/v1/register.go b/pkg/api/v1/register.go index 14b2e5adfe2..f39ae8ff12a 100644 --- a/pkg/api/v1/register.go +++ b/pkg/api/v1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" ) @@ -28,9 +27,6 @@ const GroupName = "" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1"} -// Codec encodes internal objects to the v1 scheme -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. addKnownTypes(scheme) diff --git a/pkg/api/v1/types.generated.go b/pkg/api/v1/types.generated.go index a013450798a..81ff09a6cb2 100644 --- a/pkg/api/v1/types.generated.go +++ b/pkg/api/v1/types.generated.go @@ -48803,7 +48803,7 @@ func (x codecSelfer1234) decSliceruntime_RawExtension(v *[]pkg6_runtime.RawExten yyrg3828 := len(yyv3828) > 0 yyv23828 := yyv3828 - yyrl3828, yyrt3828 = z.DecInferLen(yyl3828, z.DecBasicHandle().MaxInitLen, 24) + yyrl3828, yyrt3828 = z.DecInferLen(yyl3828, z.DecBasicHandle().MaxInitLen, 40) if yyrt3828 { if yyrl3828 <= cap(yyv3828) { yyv3828 = yyv3828[:yyrl3828] diff --git a/pkg/api/validation/schema_test.go b/pkg/api/validation/schema_test.go index f35df2bd84d..be6fc83e7a0 100644 --- a/pkg/api/validation/schema_test.go +++ b/pkg/api/validation/schema_test.go @@ -72,7 +72,7 @@ func TestValidateOk(t *testing.T) { for _, test := range tests { testObj := test.obj apiObjectFuzzer.Fuzz(testObj) - data, err := testapi.Default.Codec().Encode(testObj) + data, err := runtime.Encode(testapi.Default.Codec(), testObj) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/apis/abac/latest/latest.go b/pkg/apis/abac/latest/latest.go index bcbef485680..2618fc4fc24 100644 --- a/pkg/apis/abac/latest/latest.go +++ b/pkg/apis/abac/latest/latest.go @@ -17,8 +17,7 @@ limitations under the License. package latest import ( - "k8s.io/kubernetes/pkg/apis/abac/v1beta1" + _ "k8s.io/kubernetes/pkg/apis/abac" + _ "k8s.io/kubernetes/pkg/apis/abac/v0" + _ "k8s.io/kubernetes/pkg/apis/abac/v1beta1" ) - -// Codec is the default codec for serializing input that should use the latest supported version. -var Codec = v1beta1.Codec diff --git a/pkg/apis/abac/register.go b/pkg/apis/abac/register.go index bc99f922b18..c555d5aa979 100644 --- a/pkg/apis/abac/register.go +++ b/pkg/apis/abac/register.go @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package api +package abac import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" ) // Group is the API group for abac @@ -27,9 +28,11 @@ const Group = "abac.authorization.kubernetes.io" // Scheme is the default instance of runtime.Scheme to which types in the abac API group are registered. var Scheme = runtime.NewScheme() +// Codecs provides access to encoding and decoding for the scheme +var Codecs = serializer.NewCodecFactory(Scheme) + func init() { - Scheme.AddInternalGroupVersion(unversioned.GroupVersion{Group: Group, Version: ""}) - Scheme.AddKnownTypes(unversioned.GroupVersion{Group: Group, Version: ""}, + Scheme.AddKnownTypes(unversioned.GroupVersion{Group: Group, Version: runtime.APIVersionInternal}, &Policy{}, ) } diff --git a/pkg/apis/abac/types.go b/pkg/apis/abac/types.go index 1b694c0481b..024c7ee2417 100644 --- a/pkg/apis/abac/types.go +++ b/pkg/apis/abac/types.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package api +package abac import "k8s.io/kubernetes/pkg/api/unversioned" diff --git a/pkg/apis/abac/v0/conversion.go b/pkg/apis/abac/v0/conversion.go index 56b7a31b0c4..c0fda4bd555 100644 --- a/pkg/apis/abac/v0/conversion.go +++ b/pkg/apis/abac/v0/conversion.go @@ -17,7 +17,7 @@ limitations under the License. package v0 import ( - "k8s.io/kubernetes/pkg/apis/abac" + api "k8s.io/kubernetes/pkg/apis/abac" "k8s.io/kubernetes/pkg/conversion" ) diff --git a/pkg/apis/abac/v0/conversion_test.go b/pkg/apis/abac/v0/conversion_test.go index 256b4e25100..ffdbd398d97 100644 --- a/pkg/apis/abac/v0/conversion_test.go +++ b/pkg/apis/abac/v0/conversion_test.go @@ -20,7 +20,7 @@ import ( "reflect" "testing" - "k8s.io/kubernetes/pkg/apis/abac" + api "k8s.io/kubernetes/pkg/apis/abac" "k8s.io/kubernetes/pkg/apis/abac/v0" ) diff --git a/pkg/apis/abac/v0/register.go b/pkg/apis/abac/v0/register.go index 3aeb73df5b2..d5338045a98 100644 --- a/pkg/apis/abac/v0/register.go +++ b/pkg/apis/abac/v0/register.go @@ -18,16 +18,12 @@ package v0 import ( "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/abac" - "k8s.io/kubernetes/pkg/runtime" + api "k8s.io/kubernetes/pkg/apis/abac" ) // GroupVersion is the API group and version for abac v0 var GroupVersion = unversioned.GroupVersion{Group: api.Group, Version: "v0"} -// Codec encodes internal objects to the v0 version for the abac group -var Codec = runtime.CodecFor(api.Scheme, GroupVersion) - func init() { api.Scheme.AddKnownTypes(GroupVersion, &Policy{}, diff --git a/pkg/apis/abac/v1beta1/register.go b/pkg/apis/abac/v1beta1/register.go index 6312c762a2e..95fd6b3ef18 100644 --- a/pkg/apis/abac/v1beta1/register.go +++ b/pkg/apis/abac/v1beta1/register.go @@ -18,16 +18,12 @@ package v1beta1 import ( "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/abac" - "k8s.io/kubernetes/pkg/runtime" + api "k8s.io/kubernetes/pkg/apis/abac" ) // GroupVersion is the API group and version for abac v1beta1 var GroupVersion = unversioned.GroupVersion{Group: api.Group, Version: "v1beta1"} -// Codec encodes internal objects to the v1beta1 version for the abac group -var Codec = runtime.CodecFor(api.Scheme, GroupVersion) - func init() { api.Scheme.AddKnownTypes(GroupVersion, &Policy{}, diff --git a/pkg/apis/authorization/install/install.go b/pkg/apis/authorization/install/install.go index 83675ea6090..bf8814dd5e9 100644 --- a/pkg/apis/authorization/install/install.go +++ b/pkg/apis/authorization/install/install.go @@ -75,7 +75,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -119,7 +118,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1beta1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1beta1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/apis/authorization/register.go b/pkg/apis/authorization/register.go index 317e44c9131..b286526131a 100644 --- a/pkg/apis/authorization/register.go +++ b/pkg/apis/authorization/register.go @@ -25,7 +25,7 @@ import ( const GroupName = "authorization.k8s.io" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { diff --git a/pkg/apis/authorization/v1beta1/register.go b/pkg/apis/authorization/v1beta1/register.go index eee6eaa41c1..d9e33ed5a22 100644 --- a/pkg/apis/authorization/v1beta1/register.go +++ b/pkg/apis/authorization/v1beta1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1beta1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" ) @@ -28,8 +27,6 @@ const GroupName = "authorization.k8s.io" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1beta1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. addKnownTypes(scheme) diff --git a/pkg/apis/componentconfig/deep_copy_generated.go b/pkg/apis/componentconfig/deep_copy_generated.go index 431013d314c..c9a7b31e2f0 100644 --- a/pkg/apis/componentconfig/deep_copy_generated.go +++ b/pkg/apis/componentconfig/deep_copy_generated.go @@ -18,51 +18,10 @@ limitations under the License. package componentconfig -import ( - api "k8s.io/kubernetes/pkg/api" - unversioned "k8s.io/kubernetes/pkg/api/unversioned" - conversion "k8s.io/kubernetes/pkg/conversion" -) - -func deepCopy_unversioned_TypeMeta(in unversioned.TypeMeta, out *unversioned.TypeMeta, c *conversion.Cloner) error { - out.Kind = in.Kind - out.APIVersion = in.APIVersion - return nil -} - -func deepCopy_componentconfig_KubeProxyConfiguration(in KubeProxyConfiguration, out *KubeProxyConfiguration, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - out.BindAddress = in.BindAddress - out.CleanupIPTables = in.CleanupIPTables - out.HealthzBindAddress = in.HealthzBindAddress - out.HealthzPort = in.HealthzPort - out.HostnameOverride = in.HostnameOverride - out.IPTablesSyncePeriodSeconds = in.IPTablesSyncePeriodSeconds - out.KubeAPIBurst = in.KubeAPIBurst - out.KubeAPIQPS = in.KubeAPIQPS - out.KubeconfigPath = in.KubeconfigPath - out.MasqueradeAll = in.MasqueradeAll - out.Master = in.Master - if in.OOMScoreAdj != nil { - out.OOMScoreAdj = new(int) - *out.OOMScoreAdj = *in.OOMScoreAdj - } else { - out.OOMScoreAdj = nil - } - out.Mode = in.Mode - out.PortRange = in.PortRange - out.ResourceContainer = in.ResourceContainer - out.UDPTimeoutMilliseconds = in.UDPTimeoutMilliseconds - return nil -} +import api "k8s.io/kubernetes/pkg/api" func init() { - err := api.Scheme.AddGeneratedDeepCopyFuncs( - deepCopy_unversioned_TypeMeta, - deepCopy_componentconfig_KubeProxyConfiguration, - ) + err := api.Scheme.AddGeneratedDeepCopyFuncs() if err != nil { // if one of the deep copy functions is malformed, detect it immediately. panic(err) diff --git a/pkg/apis/componentconfig/install/install.go b/pkg/apis/componentconfig/install/install.go index 49b382dea5d..ec55542737d 100644 --- a/pkg/apis/componentconfig/install/install.go +++ b/pkg/apis/componentconfig/install/install.go @@ -75,7 +75,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -104,7 +103,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1alpha1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1alpha1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/apis/componentconfig/install/install_test.go b/pkg/apis/componentconfig/install/install_test.go index 82bae567a93..940e1756738 100644 --- a/pkg/apis/componentconfig/install/install_test.go +++ b/pkg/apis/componentconfig/install/install_test.go @@ -20,16 +20,18 @@ import ( "encoding/json" "testing" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/componentconfig" + "k8s.io/kubernetes/pkg/runtime" ) func TestCodec(t *testing.T) { daemonSet := componentconfig.KubeProxyConfiguration{} // We do want to use package registered rather than testapi here, because we // want to test if the package install and package registered work as expected. - data, err := registered.GroupOrDie(componentconfig.GroupName).Codec.Encode(&daemonSet) + data, err := runtime.Encode(api.Codecs.LegacyCodec(registered.GroupOrDie(componentconfig.GroupName).GroupVersion), &daemonSet) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -80,8 +82,8 @@ func TestRESTMapper(t *testing.T) { } interfaces, _ := registered.GroupOrDie(componentconfig.GroupName).InterfacesFor(version) - if mapping.Codec != interfaces.Codec { - t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) + if mapping.ObjectConvertor != interfaces.ObjectConvertor { + t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) } } } diff --git a/pkg/apis/componentconfig/register.go b/pkg/apis/componentconfig/register.go index ce29ed8baf4..eaf67580996 100644 --- a/pkg/apis/componentconfig/register.go +++ b/pkg/apis/componentconfig/register.go @@ -29,7 +29,7 @@ func AddToScheme(scheme *runtime.Scheme) { const GroupName = "componentconfig" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { diff --git a/pkg/apis/componentconfig/v1alpha1/register.go b/pkg/apis/componentconfig/v1alpha1/register.go index aeec8975ab4..ab0bdd5fa8f 100644 --- a/pkg/apis/componentconfig/v1alpha1/register.go +++ b/pkg/apis/componentconfig/v1alpha1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1alpha1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" ) @@ -28,8 +27,6 @@ const GroupName = "componentconfig" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1alpha1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { addKnownTypes(scheme) addDefaultingFuncs(scheme) diff --git a/pkg/apis/extensions/deep_copy_generated.go b/pkg/apis/extensions/deep_copy_generated.go index aa29f5272ba..ff5898cfe0d 100644 --- a/pkg/apis/extensions/deep_copy_generated.go +++ b/pkg/apis/extensions/deep_copy_generated.go @@ -18,1869 +18,10 @@ limitations under the License. package extensions -import ( - time "time" - - api "k8s.io/kubernetes/pkg/api" - resource "k8s.io/kubernetes/pkg/api/resource" - unversioned "k8s.io/kubernetes/pkg/api/unversioned" - conversion "k8s.io/kubernetes/pkg/conversion" - intstr "k8s.io/kubernetes/pkg/util/intstr" - inf "speter.net/go/exp/math/dec/inf" -) - -func deepCopy_api_AWSElasticBlockStoreVolumeSource(in api.AWSElasticBlockStoreVolumeSource, out *api.AWSElasticBlockStoreVolumeSource, c *conversion.Cloner) error { - out.VolumeID = in.VolumeID - out.FSType = in.FSType - out.Partition = in.Partition - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_Capabilities(in api.Capabilities, out *api.Capabilities, c *conversion.Cloner) error { - if in.Add != nil { - out.Add = make([]api.Capability, len(in.Add)) - for i := range in.Add { - out.Add[i] = in.Add[i] - } - } else { - out.Add = nil - } - if in.Drop != nil { - out.Drop = make([]api.Capability, len(in.Drop)) - for i := range in.Drop { - out.Drop[i] = in.Drop[i] - } - } else { - out.Drop = nil - } - return nil -} - -func deepCopy_api_CephFSVolumeSource(in api.CephFSVolumeSource, out *api.CephFSVolumeSource, c *conversion.Cloner) error { - if in.Monitors != nil { - out.Monitors = make([]string, len(in.Monitors)) - for i := range in.Monitors { - out.Monitors[i] = in.Monitors[i] - } - } else { - out.Monitors = nil - } - out.User = in.User - out.SecretFile = in.SecretFile - if in.SecretRef != nil { - out.SecretRef = new(api.LocalObjectReference) - if err := deepCopy_api_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { - return err - } - } else { - out.SecretRef = nil - } - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_CinderVolumeSource(in api.CinderVolumeSource, out *api.CinderVolumeSource, c *conversion.Cloner) error { - out.VolumeID = in.VolumeID - out.FSType = in.FSType - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_ConfigMapKeySelector(in api.ConfigMapKeySelector, out *api.ConfigMapKeySelector, c *conversion.Cloner) error { - if err := deepCopy_api_LocalObjectReference(in.LocalObjectReference, &out.LocalObjectReference, c); err != nil { - return err - } - out.Key = in.Key - return nil -} - -func deepCopy_api_Container(in api.Container, out *api.Container, c *conversion.Cloner) error { - out.Name = in.Name - out.Image = in.Image - if in.Command != nil { - out.Command = make([]string, len(in.Command)) - for i := range in.Command { - out.Command[i] = in.Command[i] - } - } else { - out.Command = nil - } - if in.Args != nil { - out.Args = make([]string, len(in.Args)) - for i := range in.Args { - out.Args[i] = in.Args[i] - } - } else { - out.Args = nil - } - out.WorkingDir = in.WorkingDir - if in.Ports != nil { - out.Ports = make([]api.ContainerPort, len(in.Ports)) - for i := range in.Ports { - if err := deepCopy_api_ContainerPort(in.Ports[i], &out.Ports[i], c); err != nil { - return err - } - } - } else { - out.Ports = nil - } - if in.Env != nil { - out.Env = make([]api.EnvVar, len(in.Env)) - for i := range in.Env { - if err := deepCopy_api_EnvVar(in.Env[i], &out.Env[i], c); err != nil { - return err - } - } - } else { - out.Env = nil - } - if err := deepCopy_api_ResourceRequirements(in.Resources, &out.Resources, c); err != nil { - return err - } - if in.VolumeMounts != nil { - out.VolumeMounts = make([]api.VolumeMount, len(in.VolumeMounts)) - for i := range in.VolumeMounts { - if err := deepCopy_api_VolumeMount(in.VolumeMounts[i], &out.VolumeMounts[i], c); err != nil { - return err - } - } - } else { - out.VolumeMounts = nil - } - if in.LivenessProbe != nil { - out.LivenessProbe = new(api.Probe) - if err := deepCopy_api_Probe(*in.LivenessProbe, out.LivenessProbe, c); err != nil { - return err - } - } else { - out.LivenessProbe = nil - } - if in.ReadinessProbe != nil { - out.ReadinessProbe = new(api.Probe) - if err := deepCopy_api_Probe(*in.ReadinessProbe, out.ReadinessProbe, c); err != nil { - return err - } - } else { - out.ReadinessProbe = nil - } - if in.Lifecycle != nil { - out.Lifecycle = new(api.Lifecycle) - if err := deepCopy_api_Lifecycle(*in.Lifecycle, out.Lifecycle, c); err != nil { - return err - } - } else { - out.Lifecycle = nil - } - out.TerminationMessagePath = in.TerminationMessagePath - out.ImagePullPolicy = in.ImagePullPolicy - if in.SecurityContext != nil { - out.SecurityContext = new(api.SecurityContext) - if err := deepCopy_api_SecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { - return err - } - } else { - out.SecurityContext = nil - } - out.Stdin = in.Stdin - out.StdinOnce = in.StdinOnce - out.TTY = in.TTY - return nil -} - -func deepCopy_api_ContainerPort(in api.ContainerPort, out *api.ContainerPort, c *conversion.Cloner) error { - out.Name = in.Name - out.HostPort = in.HostPort - out.ContainerPort = in.ContainerPort - out.Protocol = in.Protocol - out.HostIP = in.HostIP - return nil -} - -func deepCopy_api_DownwardAPIVolumeFile(in api.DownwardAPIVolumeFile, out *api.DownwardAPIVolumeFile, c *conversion.Cloner) error { - out.Path = in.Path - if err := deepCopy_api_ObjectFieldSelector(in.FieldRef, &out.FieldRef, c); err != nil { - return err - } - return nil -} - -func deepCopy_api_DownwardAPIVolumeSource(in api.DownwardAPIVolumeSource, out *api.DownwardAPIVolumeSource, c *conversion.Cloner) error { - if in.Items != nil { - out.Items = make([]api.DownwardAPIVolumeFile, len(in.Items)) - for i := range in.Items { - if err := deepCopy_api_DownwardAPIVolumeFile(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_api_EmptyDirVolumeSource(in api.EmptyDirVolumeSource, out *api.EmptyDirVolumeSource, c *conversion.Cloner) error { - out.Medium = in.Medium - return nil -} - -func deepCopy_api_EnvVar(in api.EnvVar, out *api.EnvVar, c *conversion.Cloner) error { - out.Name = in.Name - out.Value = in.Value - if in.ValueFrom != nil { - out.ValueFrom = new(api.EnvVarSource) - if err := deepCopy_api_EnvVarSource(*in.ValueFrom, out.ValueFrom, c); err != nil { - return err - } - } else { - out.ValueFrom = nil - } - return nil -} - -func deepCopy_api_EnvVarSource(in api.EnvVarSource, out *api.EnvVarSource, c *conversion.Cloner) error { - if in.FieldRef != nil { - out.FieldRef = new(api.ObjectFieldSelector) - if err := deepCopy_api_ObjectFieldSelector(*in.FieldRef, out.FieldRef, c); err != nil { - return err - } - } else { - out.FieldRef = nil - } - if in.ConfigMapKeyRef != nil { - out.ConfigMapKeyRef = new(api.ConfigMapKeySelector) - if err := deepCopy_api_ConfigMapKeySelector(*in.ConfigMapKeyRef, out.ConfigMapKeyRef, c); err != nil { - return err - } - } else { - out.ConfigMapKeyRef = nil - } - if in.SecretKeyRef != nil { - out.SecretKeyRef = new(api.SecretKeySelector) - if err := deepCopy_api_SecretKeySelector(*in.SecretKeyRef, out.SecretKeyRef, c); err != nil { - return err - } - } else { - out.SecretKeyRef = nil - } - return nil -} - -func deepCopy_api_ExecAction(in api.ExecAction, out *api.ExecAction, c *conversion.Cloner) error { - if in.Command != nil { - out.Command = make([]string, len(in.Command)) - for i := range in.Command { - out.Command[i] = in.Command[i] - } - } else { - out.Command = nil - } - return nil -} - -func deepCopy_api_FCVolumeSource(in api.FCVolumeSource, out *api.FCVolumeSource, c *conversion.Cloner) error { - if in.TargetWWNs != nil { - out.TargetWWNs = make([]string, len(in.TargetWWNs)) - for i := range in.TargetWWNs { - out.TargetWWNs[i] = in.TargetWWNs[i] - } - } else { - out.TargetWWNs = nil - } - if in.Lun != nil { - out.Lun = new(int) - *out.Lun = *in.Lun - } else { - out.Lun = nil - } - out.FSType = in.FSType - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_FlexVolumeSource(in api.FlexVolumeSource, out *api.FlexVolumeSource, c *conversion.Cloner) error { - out.Driver = in.Driver - out.FSType = in.FSType - if in.SecretRef != nil { - out.SecretRef = new(api.LocalObjectReference) - if err := deepCopy_api_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { - return err - } - } else { - out.SecretRef = nil - } - out.ReadOnly = in.ReadOnly - if in.Options != nil { - out.Options = make(map[string]string) - for key, val := range in.Options { - out.Options[key] = val - } - } else { - out.Options = nil - } - return nil -} - -func deepCopy_api_FlockerVolumeSource(in api.FlockerVolumeSource, out *api.FlockerVolumeSource, c *conversion.Cloner) error { - out.DatasetName = in.DatasetName - return nil -} - -func deepCopy_api_GCEPersistentDiskVolumeSource(in api.GCEPersistentDiskVolumeSource, out *api.GCEPersistentDiskVolumeSource, c *conversion.Cloner) error { - out.PDName = in.PDName - out.FSType = in.FSType - out.Partition = in.Partition - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_GitRepoVolumeSource(in api.GitRepoVolumeSource, out *api.GitRepoVolumeSource, c *conversion.Cloner) error { - out.Repository = in.Repository - out.Revision = in.Revision - out.Directory = in.Directory - return nil -} - -func deepCopy_api_GlusterfsVolumeSource(in api.GlusterfsVolumeSource, out *api.GlusterfsVolumeSource, c *conversion.Cloner) error { - out.EndpointsName = in.EndpointsName - out.Path = in.Path - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_HTTPGetAction(in api.HTTPGetAction, out *api.HTTPGetAction, c *conversion.Cloner) error { - out.Path = in.Path - if err := deepCopy_intstr_IntOrString(in.Port, &out.Port, c); err != nil { - return err - } - out.Host = in.Host - out.Scheme = in.Scheme - return nil -} - -func deepCopy_api_Handler(in api.Handler, out *api.Handler, c *conversion.Cloner) error { - if in.Exec != nil { - out.Exec = new(api.ExecAction) - if err := deepCopy_api_ExecAction(*in.Exec, out.Exec, c); err != nil { - return err - } - } else { - out.Exec = nil - } - if in.HTTPGet != nil { - out.HTTPGet = new(api.HTTPGetAction) - if err := deepCopy_api_HTTPGetAction(*in.HTTPGet, out.HTTPGet, c); err != nil { - return err - } - } else { - out.HTTPGet = nil - } - if in.TCPSocket != nil { - out.TCPSocket = new(api.TCPSocketAction) - if err := deepCopy_api_TCPSocketAction(*in.TCPSocket, out.TCPSocket, c); err != nil { - return err - } - } else { - out.TCPSocket = nil - } - return nil -} - -func deepCopy_api_HostPathVolumeSource(in api.HostPathVolumeSource, out *api.HostPathVolumeSource, c *conversion.Cloner) error { - out.Path = in.Path - return nil -} - -func deepCopy_api_ISCSIVolumeSource(in api.ISCSIVolumeSource, out *api.ISCSIVolumeSource, c *conversion.Cloner) error { - out.TargetPortal = in.TargetPortal - out.IQN = in.IQN - out.Lun = in.Lun - out.ISCSIInterface = in.ISCSIInterface - out.FSType = in.FSType - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_Lifecycle(in api.Lifecycle, out *api.Lifecycle, c *conversion.Cloner) error { - if in.PostStart != nil { - out.PostStart = new(api.Handler) - if err := deepCopy_api_Handler(*in.PostStart, out.PostStart, c); err != nil { - return err - } - } else { - out.PostStart = nil - } - if in.PreStop != nil { - out.PreStop = new(api.Handler) - if err := deepCopy_api_Handler(*in.PreStop, out.PreStop, c); err != nil { - return err - } - } else { - out.PreStop = nil - } - return nil -} - -func deepCopy_api_LoadBalancerIngress(in api.LoadBalancerIngress, out *api.LoadBalancerIngress, c *conversion.Cloner) error { - out.IP = in.IP - out.Hostname = in.Hostname - return nil -} - -func deepCopy_api_LoadBalancerStatus(in api.LoadBalancerStatus, out *api.LoadBalancerStatus, c *conversion.Cloner) error { - if in.Ingress != nil { - out.Ingress = make([]api.LoadBalancerIngress, len(in.Ingress)) - for i := range in.Ingress { - if err := deepCopy_api_LoadBalancerIngress(in.Ingress[i], &out.Ingress[i], c); err != nil { - return err - } - } - } else { - out.Ingress = nil - } - return nil -} - -func deepCopy_api_LocalObjectReference(in api.LocalObjectReference, out *api.LocalObjectReference, c *conversion.Cloner) error { - out.Name = in.Name - return nil -} - -func deepCopy_api_NFSVolumeSource(in api.NFSVolumeSource, out *api.NFSVolumeSource, c *conversion.Cloner) error { - out.Server = in.Server - out.Path = in.Path - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_ObjectFieldSelector(in api.ObjectFieldSelector, out *api.ObjectFieldSelector, c *conversion.Cloner) error { - out.APIVersion = in.APIVersion - out.FieldPath = in.FieldPath - return nil -} - -func deepCopy_api_ObjectMeta(in api.ObjectMeta, out *api.ObjectMeta, c *conversion.Cloner) error { - out.Name = in.Name - out.GenerateName = in.GenerateName - out.Namespace = in.Namespace - out.SelfLink = in.SelfLink - out.UID = in.UID - out.ResourceVersion = in.ResourceVersion - out.Generation = in.Generation - if err := deepCopy_unversioned_Time(in.CreationTimestamp, &out.CreationTimestamp, c); err != nil { - return err - } - if in.DeletionTimestamp != nil { - out.DeletionTimestamp = new(unversioned.Time) - if err := deepCopy_unversioned_Time(*in.DeletionTimestamp, out.DeletionTimestamp, c); err != nil { - return err - } - } else { - out.DeletionTimestamp = nil - } - if in.DeletionGracePeriodSeconds != nil { - out.DeletionGracePeriodSeconds = new(int64) - *out.DeletionGracePeriodSeconds = *in.DeletionGracePeriodSeconds - } else { - out.DeletionGracePeriodSeconds = nil - } - if in.Labels != nil { - out.Labels = make(map[string]string) - for key, val := range in.Labels { - out.Labels[key] = val - } - } else { - out.Labels = nil - } - if in.Annotations != nil { - out.Annotations = make(map[string]string) - for key, val := range in.Annotations { - out.Annotations[key] = val - } - } else { - out.Annotations = nil - } - return nil -} - -func deepCopy_api_PersistentVolumeClaimVolumeSource(in api.PersistentVolumeClaimVolumeSource, out *api.PersistentVolumeClaimVolumeSource, c *conversion.Cloner) error { - out.ClaimName = in.ClaimName - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_PodSecurityContext(in api.PodSecurityContext, out *api.PodSecurityContext, c *conversion.Cloner) error { - out.HostNetwork = in.HostNetwork - out.HostPID = in.HostPID - out.HostIPC = in.HostIPC - if in.SELinuxOptions != nil { - out.SELinuxOptions = new(api.SELinuxOptions) - if err := deepCopy_api_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil { - return err - } - } else { - out.SELinuxOptions = nil - } - if in.RunAsUser != nil { - out.RunAsUser = new(int64) - *out.RunAsUser = *in.RunAsUser - } else { - out.RunAsUser = nil - } - if in.RunAsNonRoot != nil { - out.RunAsNonRoot = new(bool) - *out.RunAsNonRoot = *in.RunAsNonRoot - } else { - out.RunAsNonRoot = nil - } - if in.SupplementalGroups != nil { - out.SupplementalGroups = make([]int64, len(in.SupplementalGroups)) - for i := range in.SupplementalGroups { - out.SupplementalGroups[i] = in.SupplementalGroups[i] - } - } else { - out.SupplementalGroups = nil - } - if in.FSGroup != nil { - out.FSGroup = new(int64) - *out.FSGroup = *in.FSGroup - } else { - out.FSGroup = nil - } - return nil -} - -func deepCopy_api_PodSpec(in api.PodSpec, out *api.PodSpec, c *conversion.Cloner) error { - if in.Volumes != nil { - out.Volumes = make([]api.Volume, len(in.Volumes)) - for i := range in.Volumes { - if err := deepCopy_api_Volume(in.Volumes[i], &out.Volumes[i], c); err != nil { - return err - } - } - } else { - out.Volumes = nil - } - if in.Containers != nil { - out.Containers = make([]api.Container, len(in.Containers)) - for i := range in.Containers { - if err := deepCopy_api_Container(in.Containers[i], &out.Containers[i], c); err != nil { - return err - } - } - } else { - out.Containers = nil - } - out.RestartPolicy = in.RestartPolicy - if in.TerminationGracePeriodSeconds != nil { - out.TerminationGracePeriodSeconds = new(int64) - *out.TerminationGracePeriodSeconds = *in.TerminationGracePeriodSeconds - } else { - out.TerminationGracePeriodSeconds = nil - } - if in.ActiveDeadlineSeconds != nil { - out.ActiveDeadlineSeconds = new(int64) - *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds - } else { - out.ActiveDeadlineSeconds = nil - } - out.DNSPolicy = in.DNSPolicy - if in.NodeSelector != nil { - out.NodeSelector = make(map[string]string) - for key, val := range in.NodeSelector { - out.NodeSelector[key] = val - } - } else { - out.NodeSelector = nil - } - out.ServiceAccountName = in.ServiceAccountName - out.NodeName = in.NodeName - if in.SecurityContext != nil { - out.SecurityContext = new(api.PodSecurityContext) - if err := deepCopy_api_PodSecurityContext(*in.SecurityContext, out.SecurityContext, c); err != nil { - return err - } - } else { - out.SecurityContext = nil - } - if in.ImagePullSecrets != nil { - out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets)) - for i := range in.ImagePullSecrets { - if err := deepCopy_api_LocalObjectReference(in.ImagePullSecrets[i], &out.ImagePullSecrets[i], c); err != nil { - return err - } - } - } else { - out.ImagePullSecrets = nil - } - return nil -} - -func deepCopy_api_PodTemplateSpec(in api.PodTemplateSpec, out *api.PodTemplateSpec, c *conversion.Cloner) error { - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_api_PodSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - return nil -} - -func deepCopy_api_Probe(in api.Probe, out *api.Probe, c *conversion.Cloner) error { - if err := deepCopy_api_Handler(in.Handler, &out.Handler, c); err != nil { - return err - } - out.InitialDelaySeconds = in.InitialDelaySeconds - out.TimeoutSeconds = in.TimeoutSeconds - out.PeriodSeconds = in.PeriodSeconds - out.SuccessThreshold = in.SuccessThreshold - out.FailureThreshold = in.FailureThreshold - return nil -} - -func deepCopy_api_RBDVolumeSource(in api.RBDVolumeSource, out *api.RBDVolumeSource, c *conversion.Cloner) error { - if in.CephMonitors != nil { - out.CephMonitors = make([]string, len(in.CephMonitors)) - for i := range in.CephMonitors { - out.CephMonitors[i] = in.CephMonitors[i] - } - } else { - out.CephMonitors = nil - } - out.RBDImage = in.RBDImage - out.FSType = in.FSType - out.RBDPool = in.RBDPool - out.RadosUser = in.RadosUser - out.Keyring = in.Keyring - if in.SecretRef != nil { - out.SecretRef = new(api.LocalObjectReference) - if err := deepCopy_api_LocalObjectReference(*in.SecretRef, out.SecretRef, c); err != nil { - return err - } - } else { - out.SecretRef = nil - } - out.ReadOnly = in.ReadOnly - return nil -} - -func deepCopy_api_ResourceRequirements(in api.ResourceRequirements, out *api.ResourceRequirements, c *conversion.Cloner) error { - if in.Limits != nil { - out.Limits = make(api.ResourceList) - for key, val := range in.Limits { - newVal := new(resource.Quantity) - if err := deepCopy_resource_Quantity(val, newVal, c); err != nil { - return err - } - out.Limits[key] = *newVal - } - } else { - out.Limits = nil - } - if in.Requests != nil { - out.Requests = make(api.ResourceList) - for key, val := range in.Requests { - newVal := new(resource.Quantity) - if err := deepCopy_resource_Quantity(val, newVal, c); err != nil { - return err - } - out.Requests[key] = *newVal - } - } else { - out.Requests = nil - } - return nil -} - -func deepCopy_api_SELinuxOptions(in api.SELinuxOptions, out *api.SELinuxOptions, c *conversion.Cloner) error { - out.User = in.User - out.Role = in.Role - out.Type = in.Type - out.Level = in.Level - return nil -} - -func deepCopy_api_SecretKeySelector(in api.SecretKeySelector, out *api.SecretKeySelector, c *conversion.Cloner) error { - if err := deepCopy_api_LocalObjectReference(in.LocalObjectReference, &out.LocalObjectReference, c); err != nil { - return err - } - out.Key = in.Key - return nil -} - -func deepCopy_api_SecretVolumeSource(in api.SecretVolumeSource, out *api.SecretVolumeSource, c *conversion.Cloner) error { - out.SecretName = in.SecretName - return nil -} - -func deepCopy_api_SecurityContext(in api.SecurityContext, out *api.SecurityContext, c *conversion.Cloner) error { - if in.Capabilities != nil { - out.Capabilities = new(api.Capabilities) - if err := deepCopy_api_Capabilities(*in.Capabilities, out.Capabilities, c); err != nil { - return err - } - } else { - out.Capabilities = nil - } - if in.Privileged != nil { - out.Privileged = new(bool) - *out.Privileged = *in.Privileged - } else { - out.Privileged = nil - } - if in.SELinuxOptions != nil { - out.SELinuxOptions = new(api.SELinuxOptions) - if err := deepCopy_api_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil { - return err - } - } else { - out.SELinuxOptions = nil - } - if in.RunAsUser != nil { - out.RunAsUser = new(int64) - *out.RunAsUser = *in.RunAsUser - } else { - out.RunAsUser = nil - } - if in.RunAsNonRoot != nil { - out.RunAsNonRoot = new(bool) - *out.RunAsNonRoot = *in.RunAsNonRoot - } else { - out.RunAsNonRoot = nil - } - return nil -} - -func deepCopy_api_TCPSocketAction(in api.TCPSocketAction, out *api.TCPSocketAction, c *conversion.Cloner) error { - if err := deepCopy_intstr_IntOrString(in.Port, &out.Port, c); err != nil { - return err - } - return nil -} - -func deepCopy_api_Volume(in api.Volume, out *api.Volume, c *conversion.Cloner) error { - out.Name = in.Name - if err := deepCopy_api_VolumeSource(in.VolumeSource, &out.VolumeSource, c); err != nil { - return err - } - return nil -} - -func deepCopy_api_VolumeMount(in api.VolumeMount, out *api.VolumeMount, c *conversion.Cloner) error { - out.Name = in.Name - out.ReadOnly = in.ReadOnly - out.MountPath = in.MountPath - return nil -} - -func deepCopy_api_VolumeSource(in api.VolumeSource, out *api.VolumeSource, c *conversion.Cloner) error { - if in.HostPath != nil { - out.HostPath = new(api.HostPathVolumeSource) - if err := deepCopy_api_HostPathVolumeSource(*in.HostPath, out.HostPath, c); err != nil { - return err - } - } else { - out.HostPath = nil - } - if in.EmptyDir != nil { - out.EmptyDir = new(api.EmptyDirVolumeSource) - if err := deepCopy_api_EmptyDirVolumeSource(*in.EmptyDir, out.EmptyDir, c); err != nil { - return err - } - } else { - out.EmptyDir = nil - } - if in.GCEPersistentDisk != nil { - out.GCEPersistentDisk = new(api.GCEPersistentDiskVolumeSource) - if err := deepCopy_api_GCEPersistentDiskVolumeSource(*in.GCEPersistentDisk, out.GCEPersistentDisk, c); err != nil { - return err - } - } else { - out.GCEPersistentDisk = nil - } - if in.AWSElasticBlockStore != nil { - out.AWSElasticBlockStore = new(api.AWSElasticBlockStoreVolumeSource) - if err := deepCopy_api_AWSElasticBlockStoreVolumeSource(*in.AWSElasticBlockStore, out.AWSElasticBlockStore, c); err != nil { - return err - } - } else { - out.AWSElasticBlockStore = nil - } - if in.GitRepo != nil { - out.GitRepo = new(api.GitRepoVolumeSource) - if err := deepCopy_api_GitRepoVolumeSource(*in.GitRepo, out.GitRepo, c); err != nil { - return err - } - } else { - out.GitRepo = nil - } - if in.Secret != nil { - out.Secret = new(api.SecretVolumeSource) - if err := deepCopy_api_SecretVolumeSource(*in.Secret, out.Secret, c); err != nil { - return err - } - } else { - out.Secret = nil - } - if in.NFS != nil { - out.NFS = new(api.NFSVolumeSource) - if err := deepCopy_api_NFSVolumeSource(*in.NFS, out.NFS, c); err != nil { - return err - } - } else { - out.NFS = nil - } - if in.ISCSI != nil { - out.ISCSI = new(api.ISCSIVolumeSource) - if err := deepCopy_api_ISCSIVolumeSource(*in.ISCSI, out.ISCSI, c); err != nil { - return err - } - } else { - out.ISCSI = nil - } - if in.Glusterfs != nil { - out.Glusterfs = new(api.GlusterfsVolumeSource) - if err := deepCopy_api_GlusterfsVolumeSource(*in.Glusterfs, out.Glusterfs, c); err != nil { - return err - } - } else { - out.Glusterfs = nil - } - if in.PersistentVolumeClaim != nil { - out.PersistentVolumeClaim = new(api.PersistentVolumeClaimVolumeSource) - if err := deepCopy_api_PersistentVolumeClaimVolumeSource(*in.PersistentVolumeClaim, out.PersistentVolumeClaim, c); err != nil { - return err - } - } else { - out.PersistentVolumeClaim = nil - } - if in.RBD != nil { - out.RBD = new(api.RBDVolumeSource) - if err := deepCopy_api_RBDVolumeSource(*in.RBD, out.RBD, c); err != nil { - return err - } - } else { - out.RBD = nil - } - if in.FlexVolume != nil { - out.FlexVolume = new(api.FlexVolumeSource) - if err := deepCopy_api_FlexVolumeSource(*in.FlexVolume, out.FlexVolume, c); err != nil { - return err - } - } else { - out.FlexVolume = nil - } - if in.Cinder != nil { - out.Cinder = new(api.CinderVolumeSource) - if err := deepCopy_api_CinderVolumeSource(*in.Cinder, out.Cinder, c); err != nil { - return err - } - } else { - out.Cinder = nil - } - if in.CephFS != nil { - out.CephFS = new(api.CephFSVolumeSource) - if err := deepCopy_api_CephFSVolumeSource(*in.CephFS, out.CephFS, c); err != nil { - return err - } - } else { - out.CephFS = nil - } - if in.Flocker != nil { - out.Flocker = new(api.FlockerVolumeSource) - if err := deepCopy_api_FlockerVolumeSource(*in.Flocker, out.Flocker, c); err != nil { - return err - } - } else { - out.Flocker = nil - } - if in.DownwardAPI != nil { - out.DownwardAPI = new(api.DownwardAPIVolumeSource) - if err := deepCopy_api_DownwardAPIVolumeSource(*in.DownwardAPI, out.DownwardAPI, c); err != nil { - return err - } - } else { - out.DownwardAPI = nil - } - if in.FC != nil { - out.FC = new(api.FCVolumeSource) - if err := deepCopy_api_FCVolumeSource(*in.FC, out.FC, c); err != nil { - return err - } - } else { - out.FC = nil - } - return nil -} - -func deepCopy_resource_Quantity(in resource.Quantity, out *resource.Quantity, c *conversion.Cloner) error { - if in.Amount != nil { - if newVal, err := c.DeepCopy(in.Amount); err != nil { - return err - } else { - out.Amount = newVal.(*inf.Dec) - } - } else { - out.Amount = nil - } - out.Format = in.Format - return nil -} - -func deepCopy_unversioned_ListMeta(in unversioned.ListMeta, out *unversioned.ListMeta, c *conversion.Cloner) error { - out.SelfLink = in.SelfLink - out.ResourceVersion = in.ResourceVersion - return nil -} - -func deepCopy_unversioned_Time(in unversioned.Time, out *unversioned.Time, c *conversion.Cloner) error { - if newVal, err := c.DeepCopy(in.Time); err != nil { - return err - } else { - out.Time = newVal.(time.Time) - } - return nil -} - -func deepCopy_unversioned_TypeMeta(in unversioned.TypeMeta, out *unversioned.TypeMeta, c *conversion.Cloner) error { - out.Kind = in.Kind - out.APIVersion = in.APIVersion - return nil -} - -func deepCopy_extensions_APIVersion(in APIVersion, out *APIVersion, c *conversion.Cloner) error { - out.Name = in.Name - out.APIGroup = in.APIGroup - return nil -} - -func deepCopy_extensions_CPUTargetUtilization(in CPUTargetUtilization, out *CPUTargetUtilization, c *conversion.Cloner) error { - out.TargetPercentage = in.TargetPercentage - return nil -} - -func deepCopy_extensions_ClusterAutoscaler(in ClusterAutoscaler, out *ClusterAutoscaler, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_ClusterAutoscalerSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_ClusterAutoscalerList(in ClusterAutoscalerList, out *ClusterAutoscalerList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ClusterAutoscaler, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ClusterAutoscaler(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_ClusterAutoscalerSpec(in ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, c *conversion.Cloner) error { - out.MinNodes = in.MinNodes - out.MaxNodes = in.MaxNodes - if in.TargetUtilization != nil { - out.TargetUtilization = make([]NodeUtilization, len(in.TargetUtilization)) - for i := range in.TargetUtilization { - if err := deepCopy_extensions_NodeUtilization(in.TargetUtilization[i], &out.TargetUtilization[i], c); err != nil { - return err - } - } - } else { - out.TargetUtilization = nil - } - return nil -} - -func deepCopy_extensions_ConfigMap(in ConfigMap, out *ConfigMap, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if in.Data != nil { - out.Data = make(map[string]string) - for key, val := range in.Data { - out.Data[key] = val - } - } else { - out.Data = nil - } - return nil -} - -func deepCopy_extensions_ConfigMapList(in ConfigMapList, out *ConfigMapList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ConfigMap, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ConfigMap(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_DaemonSet(in DaemonSet, out *DaemonSet, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_DaemonSetSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_DaemonSetStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_DaemonSetList(in DaemonSetList, out *DaemonSetList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]DaemonSet, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_DaemonSet(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_DaemonSetSpec(in DaemonSetSpec, out *DaemonSetSpec, c *conversion.Cloner) error { - if in.Selector != nil { - out.Selector = new(LabelSelector) - if err := deepCopy_extensions_LabelSelector(*in.Selector, out.Selector, c); err != nil { - return err - } - } else { - out.Selector = nil - } - if in.Template != nil { - out.Template = new(api.PodTemplateSpec) - if err := deepCopy_api_PodTemplateSpec(*in.Template, out.Template, c); err != nil { - return err - } - } else { - out.Template = nil - } - if err := deepCopy_extensions_DaemonSetUpdateStrategy(in.UpdateStrategy, &out.UpdateStrategy, c); err != nil { - return err - } - out.UniqueLabelKey = in.UniqueLabelKey - return nil -} - -func deepCopy_extensions_DaemonSetStatus(in DaemonSetStatus, out *DaemonSetStatus, c *conversion.Cloner) error { - out.CurrentNumberScheduled = in.CurrentNumberScheduled - out.NumberMisscheduled = in.NumberMisscheduled - out.DesiredNumberScheduled = in.DesiredNumberScheduled - return nil -} - -func deepCopy_extensions_DaemonSetUpdateStrategy(in DaemonSetUpdateStrategy, out *DaemonSetUpdateStrategy, c *conversion.Cloner) error { - out.Type = in.Type - if in.RollingUpdate != nil { - out.RollingUpdate = new(RollingUpdateDaemonSet) - if err := deepCopy_extensions_RollingUpdateDaemonSet(*in.RollingUpdate, out.RollingUpdate, c); err != nil { - return err - } - } else { - out.RollingUpdate = nil - } - return nil -} - -func deepCopy_extensions_Deployment(in Deployment, out *Deployment, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_DeploymentSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_DeploymentStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_DeploymentList(in DeploymentList, out *DeploymentList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]Deployment, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_Deployment(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_DeploymentSpec(in DeploymentSpec, out *DeploymentSpec, c *conversion.Cloner) error { - out.Replicas = in.Replicas - if in.Selector != nil { - out.Selector = make(map[string]string) - for key, val := range in.Selector { - out.Selector[key] = val - } - } else { - out.Selector = nil - } - if err := deepCopy_api_PodTemplateSpec(in.Template, &out.Template, c); err != nil { - return err - } - if err := deepCopy_extensions_DeploymentStrategy(in.Strategy, &out.Strategy, c); err != nil { - return err - } - out.UniqueLabelKey = in.UniqueLabelKey - return nil -} - -func deepCopy_extensions_DeploymentStatus(in DeploymentStatus, out *DeploymentStatus, c *conversion.Cloner) error { - out.Replicas = in.Replicas - out.UpdatedReplicas = in.UpdatedReplicas - return nil -} - -func deepCopy_extensions_DeploymentStrategy(in DeploymentStrategy, out *DeploymentStrategy, c *conversion.Cloner) error { - out.Type = in.Type - if in.RollingUpdate != nil { - out.RollingUpdate = new(RollingUpdateDeployment) - if err := deepCopy_extensions_RollingUpdateDeployment(*in.RollingUpdate, out.RollingUpdate, c); err != nil { - return err - } - } else { - out.RollingUpdate = nil - } - return nil -} - -func deepCopy_extensions_HTTPIngressPath(in HTTPIngressPath, out *HTTPIngressPath, c *conversion.Cloner) error { - out.Path = in.Path - if err := deepCopy_extensions_IngressBackend(in.Backend, &out.Backend, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_HTTPIngressRuleValue(in HTTPIngressRuleValue, out *HTTPIngressRuleValue, c *conversion.Cloner) error { - if in.Paths != nil { - out.Paths = make([]HTTPIngressPath, len(in.Paths)) - for i := range in.Paths { - if err := deepCopy_extensions_HTTPIngressPath(in.Paths[i], &out.Paths[i], c); err != nil { - return err - } - } - } else { - out.Paths = nil - } - return nil -} - -func deepCopy_extensions_HorizontalPodAutoscaler(in HorizontalPodAutoscaler, out *HorizontalPodAutoscaler, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_HorizontalPodAutoscalerSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_HorizontalPodAutoscalerStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_HorizontalPodAutoscalerList(in HorizontalPodAutoscalerList, out *HorizontalPodAutoscalerList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]HorizontalPodAutoscaler, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_HorizontalPodAutoscaler(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_HorizontalPodAutoscalerSpec(in HorizontalPodAutoscalerSpec, out *HorizontalPodAutoscalerSpec, c *conversion.Cloner) error { - if err := deepCopy_extensions_SubresourceReference(in.ScaleRef, &out.ScaleRef, c); err != nil { - return err - } - if in.MinReplicas != nil { - out.MinReplicas = new(int) - *out.MinReplicas = *in.MinReplicas - } else { - out.MinReplicas = nil - } - out.MaxReplicas = in.MaxReplicas - if in.CPUUtilization != nil { - out.CPUUtilization = new(CPUTargetUtilization) - if err := deepCopy_extensions_CPUTargetUtilization(*in.CPUUtilization, out.CPUUtilization, c); err != nil { - return err - } - } else { - out.CPUUtilization = nil - } - return nil -} - -func deepCopy_extensions_HorizontalPodAutoscalerStatus(in HorizontalPodAutoscalerStatus, out *HorizontalPodAutoscalerStatus, c *conversion.Cloner) error { - if in.ObservedGeneration != nil { - out.ObservedGeneration = new(int64) - *out.ObservedGeneration = *in.ObservedGeneration - } else { - out.ObservedGeneration = nil - } - if in.LastScaleTime != nil { - out.LastScaleTime = new(unversioned.Time) - if err := deepCopy_unversioned_Time(*in.LastScaleTime, out.LastScaleTime, c); err != nil { - return err - } - } else { - out.LastScaleTime = nil - } - out.CurrentReplicas = in.CurrentReplicas - out.DesiredReplicas = in.DesiredReplicas - if in.CurrentCPUUtilizationPercentage != nil { - out.CurrentCPUUtilizationPercentage = new(int) - *out.CurrentCPUUtilizationPercentage = *in.CurrentCPUUtilizationPercentage - } else { - out.CurrentCPUUtilizationPercentage = nil - } - return nil -} - -func deepCopy_extensions_Ingress(in Ingress, out *Ingress, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_IngressSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_IngressStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_IngressBackend(in IngressBackend, out *IngressBackend, c *conversion.Cloner) error { - out.ServiceName = in.ServiceName - if err := deepCopy_intstr_IntOrString(in.ServicePort, &out.ServicePort, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_IngressList(in IngressList, out *IngressList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]Ingress, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_Ingress(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_IngressRule(in IngressRule, out *IngressRule, c *conversion.Cloner) error { - out.Host = in.Host - if err := deepCopy_extensions_IngressRuleValue(in.IngressRuleValue, &out.IngressRuleValue, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_IngressRuleValue(in IngressRuleValue, out *IngressRuleValue, c *conversion.Cloner) error { - if in.HTTP != nil { - out.HTTP = new(HTTPIngressRuleValue) - if err := deepCopy_extensions_HTTPIngressRuleValue(*in.HTTP, out.HTTP, c); err != nil { - return err - } - } else { - out.HTTP = nil - } - return nil -} - -func deepCopy_extensions_IngressSpec(in IngressSpec, out *IngressSpec, c *conversion.Cloner) error { - if in.Backend != nil { - out.Backend = new(IngressBackend) - if err := deepCopy_extensions_IngressBackend(*in.Backend, out.Backend, c); err != nil { - return err - } - } else { - out.Backend = nil - } - if in.Rules != nil { - out.Rules = make([]IngressRule, len(in.Rules)) - for i := range in.Rules { - if err := deepCopy_extensions_IngressRule(in.Rules[i], &out.Rules[i], c); err != nil { - return err - } - } - } else { - out.Rules = nil - } - return nil -} - -func deepCopy_extensions_IngressStatus(in IngressStatus, out *IngressStatus, c *conversion.Cloner) error { - if err := deepCopy_api_LoadBalancerStatus(in.LoadBalancer, &out.LoadBalancer, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_Job(in Job, out *Job, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_JobSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_JobStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_JobCondition(in JobCondition, out *JobCondition, c *conversion.Cloner) error { - out.Type = in.Type - out.Status = in.Status - if err := deepCopy_unversioned_Time(in.LastProbeTime, &out.LastProbeTime, c); err != nil { - return err - } - if err := deepCopy_unversioned_Time(in.LastTransitionTime, &out.LastTransitionTime, c); err != nil { - return err - } - out.Reason = in.Reason - out.Message = in.Message - return nil -} - -func deepCopy_extensions_JobList(in JobList, out *JobList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]Job, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_Job(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_JobSpec(in JobSpec, out *JobSpec, c *conversion.Cloner) error { - if in.Parallelism != nil { - out.Parallelism = new(int) - *out.Parallelism = *in.Parallelism - } else { - out.Parallelism = nil - } - if in.Completions != nil { - out.Completions = new(int) - *out.Completions = *in.Completions - } else { - out.Completions = nil - } - if in.ActiveDeadlineSeconds != nil { - out.ActiveDeadlineSeconds = new(int64) - *out.ActiveDeadlineSeconds = *in.ActiveDeadlineSeconds - } else { - out.ActiveDeadlineSeconds = nil - } - if in.Selector != nil { - out.Selector = new(LabelSelector) - if err := deepCopy_extensions_LabelSelector(*in.Selector, out.Selector, c); err != nil { - return err - } - } else { - out.Selector = nil - } - if err := deepCopy_api_PodTemplateSpec(in.Template, &out.Template, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_JobStatus(in JobStatus, out *JobStatus, c *conversion.Cloner) error { - if in.Conditions != nil { - out.Conditions = make([]JobCondition, len(in.Conditions)) - for i := range in.Conditions { - if err := deepCopy_extensions_JobCondition(in.Conditions[i], &out.Conditions[i], c); err != nil { - return err - } - } - } else { - out.Conditions = nil - } - if in.StartTime != nil { - out.StartTime = new(unversioned.Time) - if err := deepCopy_unversioned_Time(*in.StartTime, out.StartTime, c); err != nil { - return err - } - } else { - out.StartTime = nil - } - if in.CompletionTime != nil { - out.CompletionTime = new(unversioned.Time) - if err := deepCopy_unversioned_Time(*in.CompletionTime, out.CompletionTime, c); err != nil { - return err - } - } else { - out.CompletionTime = nil - } - out.Active = in.Active - out.Succeeded = in.Succeeded - out.Failed = in.Failed - return nil -} - -func deepCopy_extensions_LabelSelector(in LabelSelector, out *LabelSelector, c *conversion.Cloner) error { - if in.MatchLabels != nil { - out.MatchLabels = make(map[string]string) - for key, val := range in.MatchLabels { - out.MatchLabels[key] = val - } - } else { - out.MatchLabels = nil - } - if in.MatchExpressions != nil { - out.MatchExpressions = make([]LabelSelectorRequirement, len(in.MatchExpressions)) - for i := range in.MatchExpressions { - if err := deepCopy_extensions_LabelSelectorRequirement(in.MatchExpressions[i], &out.MatchExpressions[i], c); err != nil { - return err - } - } - } else { - out.MatchExpressions = nil - } - return nil -} - -func deepCopy_extensions_LabelSelectorRequirement(in LabelSelectorRequirement, out *LabelSelectorRequirement, c *conversion.Cloner) error { - out.Key = in.Key - out.Operator = in.Operator - if in.Values != nil { - out.Values = make([]string, len(in.Values)) - for i := range in.Values { - out.Values[i] = in.Values[i] - } - } else { - out.Values = nil - } - return nil -} - -func deepCopy_extensions_NodeUtilization(in NodeUtilization, out *NodeUtilization, c *conversion.Cloner) error { - out.Resource = in.Resource - out.Value = in.Value - return nil -} - -func deepCopy_extensions_ReplicaSet(in ReplicaSet, out *ReplicaSet, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_ReplicaSetSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_ReplicaSetStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_ReplicaSetList(in ReplicaSetList, out *ReplicaSetList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ReplicaSet, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ReplicaSet(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_ReplicaSetSpec(in ReplicaSetSpec, out *ReplicaSetSpec, c *conversion.Cloner) error { - out.Replicas = in.Replicas - if in.Selector != nil { - out.Selector = new(LabelSelector) - if err := deepCopy_extensions_LabelSelector(*in.Selector, out.Selector, c); err != nil { - return err - } - } else { - out.Selector = nil - } - if in.Template != nil { - out.Template = new(api.PodTemplateSpec) - if err := deepCopy_api_PodTemplateSpec(*in.Template, out.Template, c); err != nil { - return err - } - } else { - out.Template = nil - } - return nil -} - -func deepCopy_extensions_ReplicaSetStatus(in ReplicaSetStatus, out *ReplicaSetStatus, c *conversion.Cloner) error { - out.Replicas = in.Replicas - out.ObservedGeneration = in.ObservedGeneration - return nil -} - -func deepCopy_extensions_ReplicationControllerDummy(in ReplicationControllerDummy, out *ReplicationControllerDummy, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_RollingUpdateDaemonSet(in RollingUpdateDaemonSet, out *RollingUpdateDaemonSet, c *conversion.Cloner) error { - if err := deepCopy_intstr_IntOrString(in.MaxUnavailable, &out.MaxUnavailable, c); err != nil { - return err - } - out.MinReadySeconds = in.MinReadySeconds - return nil -} - -func deepCopy_extensions_RollingUpdateDeployment(in RollingUpdateDeployment, out *RollingUpdateDeployment, c *conversion.Cloner) error { - if err := deepCopy_intstr_IntOrString(in.MaxUnavailable, &out.MaxUnavailable, c); err != nil { - return err - } - if err := deepCopy_intstr_IntOrString(in.MaxSurge, &out.MaxSurge, c); err != nil { - return err - } - out.MinReadySeconds = in.MinReadySeconds - return nil -} - -func deepCopy_extensions_Scale(in Scale, out *Scale, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if err := deepCopy_extensions_ScaleSpec(in.Spec, &out.Spec, c); err != nil { - return err - } - if err := deepCopy_extensions_ScaleStatus(in.Status, &out.Status, c); err != nil { - return err - } - return nil -} - -func deepCopy_extensions_ScaleSpec(in ScaleSpec, out *ScaleSpec, c *conversion.Cloner) error { - out.Replicas = in.Replicas - return nil -} - -func deepCopy_extensions_ScaleStatus(in ScaleStatus, out *ScaleStatus, c *conversion.Cloner) error { - out.Replicas = in.Replicas - if in.Selector != nil { - out.Selector = make(map[string]string) - for key, val := range in.Selector { - out.Selector[key] = val - } - } else { - out.Selector = nil - } - return nil -} - -func deepCopy_extensions_SubresourceReference(in SubresourceReference, out *SubresourceReference, c *conversion.Cloner) error { - out.Kind = in.Kind - out.Name = in.Name - out.APIVersion = in.APIVersion - out.Subresource = in.Subresource - return nil -} - -func deepCopy_extensions_ThirdPartyResource(in ThirdPartyResource, out *ThirdPartyResource, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - out.Description = in.Description - if in.Versions != nil { - out.Versions = make([]APIVersion, len(in.Versions)) - for i := range in.Versions { - if err := deepCopy_extensions_APIVersion(in.Versions[i], &out.Versions[i], c); err != nil { - return err - } - } - } else { - out.Versions = nil - } - return nil -} - -func deepCopy_extensions_ThirdPartyResourceData(in ThirdPartyResourceData, out *ThirdPartyResourceData, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { - return err - } - if in.Data != nil { - out.Data = make([]uint8, len(in.Data)) - for i := range in.Data { - out.Data[i] = in.Data[i] - } - } else { - out.Data = nil - } - return nil -} - -func deepCopy_extensions_ThirdPartyResourceDataList(in ThirdPartyResourceDataList, out *ThirdPartyResourceDataList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ThirdPartyResourceData, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ThirdPartyResourceData(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_extensions_ThirdPartyResourceList(in ThirdPartyResourceList, out *ThirdPartyResourceList, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { - return err - } - if in.Items != nil { - out.Items = make([]ThirdPartyResource, len(in.Items)) - for i := range in.Items { - if err := deepCopy_extensions_ThirdPartyResource(in.Items[i], &out.Items[i], c); err != nil { - return err - } - } - } else { - out.Items = nil - } - return nil -} - -func deepCopy_intstr_IntOrString(in intstr.IntOrString, out *intstr.IntOrString, c *conversion.Cloner) error { - out.Type = in.Type - out.IntVal = in.IntVal - out.StrVal = in.StrVal - return nil -} +import api "k8s.io/kubernetes/pkg/api" func init() { - err := api.Scheme.AddGeneratedDeepCopyFuncs( - deepCopy_api_AWSElasticBlockStoreVolumeSource, - deepCopy_api_Capabilities, - deepCopy_api_CephFSVolumeSource, - deepCopy_api_CinderVolumeSource, - deepCopy_api_ConfigMapKeySelector, - deepCopy_api_Container, - deepCopy_api_ContainerPort, - deepCopy_api_DownwardAPIVolumeFile, - deepCopy_api_DownwardAPIVolumeSource, - deepCopy_api_EmptyDirVolumeSource, - deepCopy_api_EnvVar, - deepCopy_api_EnvVarSource, - deepCopy_api_ExecAction, - deepCopy_api_FCVolumeSource, - deepCopy_api_FlexVolumeSource, - deepCopy_api_FlockerVolumeSource, - deepCopy_api_GCEPersistentDiskVolumeSource, - deepCopy_api_GitRepoVolumeSource, - deepCopy_api_GlusterfsVolumeSource, - deepCopy_api_HTTPGetAction, - deepCopy_api_Handler, - deepCopy_api_HostPathVolumeSource, - deepCopy_api_ISCSIVolumeSource, - deepCopy_api_Lifecycle, - deepCopy_api_LoadBalancerIngress, - deepCopy_api_LoadBalancerStatus, - deepCopy_api_LocalObjectReference, - deepCopy_api_NFSVolumeSource, - deepCopy_api_ObjectFieldSelector, - deepCopy_api_ObjectMeta, - deepCopy_api_PersistentVolumeClaimVolumeSource, - deepCopy_api_PodSecurityContext, - deepCopy_api_PodSpec, - deepCopy_api_PodTemplateSpec, - deepCopy_api_Probe, - deepCopy_api_RBDVolumeSource, - deepCopy_api_ResourceRequirements, - deepCopy_api_SELinuxOptions, - deepCopy_api_SecretKeySelector, - deepCopy_api_SecretVolumeSource, - deepCopy_api_SecurityContext, - deepCopy_api_TCPSocketAction, - deepCopy_api_Volume, - deepCopy_api_VolumeMount, - deepCopy_api_VolumeSource, - deepCopy_resource_Quantity, - deepCopy_unversioned_ListMeta, - deepCopy_unversioned_Time, - deepCopy_unversioned_TypeMeta, - deepCopy_extensions_APIVersion, - deepCopy_extensions_CPUTargetUtilization, - deepCopy_extensions_ClusterAutoscaler, - deepCopy_extensions_ClusterAutoscalerList, - deepCopy_extensions_ClusterAutoscalerSpec, - deepCopy_extensions_ConfigMap, - deepCopy_extensions_ConfigMapList, - deepCopy_extensions_DaemonSet, - deepCopy_extensions_DaemonSetList, - deepCopy_extensions_DaemonSetSpec, - deepCopy_extensions_DaemonSetStatus, - deepCopy_extensions_DaemonSetUpdateStrategy, - deepCopy_extensions_Deployment, - deepCopy_extensions_DeploymentList, - deepCopy_extensions_DeploymentSpec, - deepCopy_extensions_DeploymentStatus, - deepCopy_extensions_DeploymentStrategy, - deepCopy_extensions_HTTPIngressPath, - deepCopy_extensions_HTTPIngressRuleValue, - deepCopy_extensions_HorizontalPodAutoscaler, - deepCopy_extensions_HorizontalPodAutoscalerList, - deepCopy_extensions_HorizontalPodAutoscalerSpec, - deepCopy_extensions_HorizontalPodAutoscalerStatus, - deepCopy_extensions_Ingress, - deepCopy_extensions_IngressBackend, - deepCopy_extensions_IngressList, - deepCopy_extensions_IngressRule, - deepCopy_extensions_IngressRuleValue, - deepCopy_extensions_IngressSpec, - deepCopy_extensions_IngressStatus, - deepCopy_extensions_Job, - deepCopy_extensions_JobCondition, - deepCopy_extensions_JobList, - deepCopy_extensions_JobSpec, - deepCopy_extensions_JobStatus, - deepCopy_extensions_LabelSelector, - deepCopy_extensions_LabelSelectorRequirement, - deepCopy_extensions_NodeUtilization, - deepCopy_extensions_ReplicaSet, - deepCopy_extensions_ReplicaSetList, - deepCopy_extensions_ReplicaSetSpec, - deepCopy_extensions_ReplicaSetStatus, - deepCopy_extensions_ReplicationControllerDummy, - deepCopy_extensions_RollingUpdateDaemonSet, - deepCopy_extensions_RollingUpdateDeployment, - deepCopy_extensions_Scale, - deepCopy_extensions_ScaleSpec, - deepCopy_extensions_ScaleStatus, - deepCopy_extensions_SubresourceReference, - deepCopy_extensions_ThirdPartyResource, - deepCopy_extensions_ThirdPartyResourceData, - deepCopy_extensions_ThirdPartyResourceDataList, - deepCopy_extensions_ThirdPartyResourceList, - deepCopy_intstr_IntOrString, - ) + err := api.Scheme.AddGeneratedDeepCopyFuncs() if err != nil { // if one of the deep copy functions is malformed, detect it immediately. panic(err) diff --git a/pkg/apis/extensions/install/install.go b/pkg/apis/extensions/install/install.go index bcc10f36003..4e1477d2d0a 100644 --- a/pkg/apis/extensions/install/install.go +++ b/pkg/apis/extensions/install/install.go @@ -75,7 +75,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -104,7 +103,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1beta1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1beta1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/apis/extensions/install/install_test.go b/pkg/apis/extensions/install/install_test.go index 4196d2b669c..311fad56da9 100644 --- a/pkg/apis/extensions/install/install_test.go +++ b/pkg/apis/extensions/install/install_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + "k8s.io/kubernetes/pkg/runtime" ) func TestResourceVersioner(t *testing.T) { @@ -51,7 +52,7 @@ func TestCodec(t *testing.T) { daemonSet := extensions.DaemonSet{} // We do want to use package registered rather than testapi here, because we // want to test if the package install and package registered work as expected. - data, err := registered.GroupOrDie(extensions.GroupName).Codec.Encode(&daemonSet) + data, err := runtime.Encode(api.Codecs.LegacyCodec(registered.GroupOrDie(extensions.GroupName).GroupVersion), &daemonSet) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -102,8 +103,8 @@ func TestRESTMapper(t *testing.T) { } interfaces, _ := registered.GroupOrDie(extensions.GroupName).InterfacesFor(version) - if mapping.Codec != interfaces.Codec { - t.Errorf("unexpected codec: %#v, expected: %#v", mapping, interfaces) + if mapping.ObjectConvertor != interfaces.ObjectConvertor { + t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces) } rc := &extensions.HorizontalPodAutoscaler{ObjectMeta: api.ObjectMeta{Name: "foo"}} diff --git a/pkg/apis/extensions/register.go b/pkg/apis/extensions/register.go index f33957292e4..d02dd6f6ea6 100644 --- a/pkg/apis/extensions/register.go +++ b/pkg/apis/extensions/register.go @@ -26,7 +26,7 @@ import ( const GroupName = "extensions" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { diff --git a/pkg/apis/extensions/v1beta1/defaults_test.go b/pkg/apis/extensions/v1beta1/defaults_test.go index 082fc2adb41..bce6e1997de 100644 --- a/pkg/apis/extensions/v1beta1/defaults_test.go +++ b/pkg/apis/extensions/v1beta1/defaults_test.go @@ -588,12 +588,12 @@ func TestDefaultRequestIsNotSetForReplicaSet(t *testing.T) { } func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { - data, err := runtime.Encode(Codec, obj) + data, err := runtime.Encode(api.Codecs.LegacyCodec(SchemeGroupVersion), obj) if err != nil { t.Errorf("%v\n %#v", err, obj) return nil } - obj2, err := api.Codec.Decode(data) + obj2, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) if err != nil { t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) return nil diff --git a/pkg/apis/extensions/v1beta1/register.go b/pkg/apis/extensions/v1beta1/register.go index e4bbf61da8c..8ffbc48e635 100644 --- a/pkg/apis/extensions/v1beta1/register.go +++ b/pkg/apis/extensions/v1beta1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1beta1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" @@ -29,8 +28,6 @@ const GroupName = "extensions" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1beta1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { addKnownTypes(scheme) addDefaultingFuncs(scheme) diff --git a/pkg/apis/metrics/deep_copy_generated.go b/pkg/apis/metrics/deep_copy_generated.go index 1de6b1d4c0b..e7562de5221 100644 --- a/pkg/apis/metrics/deep_copy_generated.go +++ b/pkg/apis/metrics/deep_copy_generated.go @@ -18,38 +18,10 @@ limitations under the License. package metrics -import ( - api "k8s.io/kubernetes/pkg/api" - unversioned "k8s.io/kubernetes/pkg/api/unversioned" - conversion "k8s.io/kubernetes/pkg/conversion" -) - -func deepCopy_unversioned_TypeMeta(in unversioned.TypeMeta, out *unversioned.TypeMeta, c *conversion.Cloner) error { - out.Kind = in.Kind - out.APIVersion = in.APIVersion - return nil -} - -func deepCopy_metrics_RawNode(in RawNode, out *RawNode, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - return nil -} - -func deepCopy_metrics_RawPod(in RawPod, out *RawPod, c *conversion.Cloner) error { - if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { - return err - } - return nil -} +import api "k8s.io/kubernetes/pkg/api" func init() { - err := api.Scheme.AddGeneratedDeepCopyFuncs( - deepCopy_unversioned_TypeMeta, - deepCopy_metrics_RawNode, - deepCopy_metrics_RawPod, - ) + err := api.Scheme.AddGeneratedDeepCopyFuncs() if err != nil { // if one of the deep copy functions is malformed, detect it immediately. panic(err) diff --git a/pkg/apis/metrics/install/install.go b/pkg/apis/metrics/install/install.go index 75709fa6e21..e83c91cc4a2 100644 --- a/pkg/apis/metrics/install/install.go +++ b/pkg/apis/metrics/install/install.go @@ -75,7 +75,6 @@ func enableVersions(externalVersions []unversioned.GroupVersion) error { groupMeta := apimachinery.GroupMeta{ GroupVersion: preferredExternalVersion, GroupVersions: externalVersions, - Codec: runtime.CodecFor(api.Scheme, preferredExternalVersion), RESTMapper: newRESTMapper(externalVersions), SelfLinker: runtime.SelfLinker(accessor), InterfacesFor: interfacesFor, @@ -104,7 +103,6 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case v1alpha1.SchemeGroupVersion: return &meta.VersionInterfaces{ - Codec: v1alpha1.Codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil diff --git a/pkg/apis/metrics/register.go b/pkg/apis/metrics/register.go index 99bcb529c14..29ad86a09f3 100644 --- a/pkg/apis/metrics/register.go +++ b/pkg/apis/metrics/register.go @@ -30,7 +30,7 @@ func AddToScheme(scheme *runtime.Scheme) { const GroupName = "metrics" // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) unversioned.GroupKind { diff --git a/pkg/apis/metrics/v1alpha1/register.go b/pkg/apis/metrics/v1alpha1/register.go index 1643f6e7e47..c943d54687d 100644 --- a/pkg/apis/metrics/v1alpha1/register.go +++ b/pkg/apis/metrics/v1alpha1/register.go @@ -17,7 +17,6 @@ limitations under the License. package v1alpha1 import ( - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" @@ -29,8 +28,6 @@ const GroupName = "metrics" // SchemeGroupVersion is group version used to register these objects var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1alpha1"} -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func AddToScheme(scheme *runtime.Scheme) { // Add the API to Scheme. addKnownTypes(scheme) diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index 10126e5f691..819cec6a198 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -65,7 +65,13 @@ var errEmptyName = errors.NewBadRequest("name must be provided") func (a *APIInstaller) Install(ws *restful.WebService) (apiResources []unversioned.APIResource, errors []error) { errors = make([]error, 0) - proxyHandler := (&ProxyHandler{a.prefix + "/proxy/", a.group.Storage, a.group.Codec, a.group.Context, a.info}) + proxyHandler := (&ProxyHandler{ + prefix: a.prefix + "/proxy/", + storage: a.group.Storage, + serializer: a.group.Serializer, + context: a.group.Context, + requestInfoResolver: a.info, + }) // Register the paths in a deterministic (sorted) order to get a deterministic swagger spec. paths := make([]string, len(a.group.Storage)) @@ -93,9 +99,11 @@ func (a *APIInstaller) NewWebService() *restful.WebService { ws.Path(a.prefix) // a.prefix contains "prefix/group/version" ws.Doc("API at " + a.prefix) - // TODO: change to restful.MIME_JSON when we set content type in client + // Backwards compatibilty, we accepted objects with empty content-type at V1. + // If we stop using go-restful, we can default empty content-type to application/json on an + // endpoint by endpoint basis ws.Consumes("*/*") - ws.Produces(restful.MIME_JSON) + ws.Produces(a.group.Serializer.SupportedMediaTypes()...) ws.ApiVersion(a.group.GroupVersion.String()) return ws @@ -262,19 +270,14 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag getOptions runtime.Object versionedGetOptions runtime.Object getOptionsInternalKind unversioned.GroupVersionKind - getOptionsExternalKind unversioned.GroupVersionKind getSubpath bool - getSubpathKey string ) if isGetterWithOptions { - getOptions, getSubpath, getSubpathKey = getterWithOptions.NewGetOptions() + getOptions, getSubpath, _ = getterWithOptions.NewGetOptions() getOptionsInternalKind, err = a.group.Typer.ObjectKind(getOptions) if err != nil { return nil, err } - // TODO this should be a list of all the different external versions we can coerce into the internalKind - getOptionsExternalKind = optionsExternalVersion.WithKind(getOptionsInternalKind.Kind) - versionedGetOptions, err = a.group.Creater.New(optionsExternalVersion.WithKind(getOptionsInternalKind.Kind)) if err != nil { return nil, err @@ -286,19 +289,15 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag connectOptions runtime.Object versionedConnectOptions runtime.Object connectOptionsInternalKind unversioned.GroupVersionKind - connectOptionsExternalKind unversioned.GroupVersionKind connectSubpath bool - connectSubpathKey string ) if isConnecter { - connectOptions, connectSubpath, connectSubpathKey = connecter.NewConnectOptions() + connectOptions, connectSubpath, _ = connecter.NewConnectOptions() if connectOptions != nil { connectOptionsInternalKind, err = a.group.Typer.ObjectKind(connectOptions) if err != nil { return nil, err } - // TODO this should be a list of all the different external versions we can coerce into the internalKind - connectOptionsExternalKind = optionsExternalVersion.WithKind(connectOptionsInternalKind.Kind) versionedConnectOptions, err = a.group.Creater.New(optionsExternalVersion.WithKind(connectOptionsInternalKind.Kind)) } @@ -434,10 +433,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag // test/integration/auth_test.go is currently the most comprehensive status code test reqScope := RequestScope{ - ContextFunc: ctxFn, - Creater: a.group.Creater, - Convertor: a.group.Convertor, - Codec: mapping.Codec, + ContextFunc: ctxFn, + Serializer: a.group.Serializer, + ParameterCodec: a.group.ParameterCodec, + Creater: a.group.Creater, + Convertor: a.group.Convertor, Resource: a.group.GroupVersion.WithResource(resource), Subresource: subresource, @@ -454,7 +454,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag case "GET": // Get a resource. var handler restful.RouteFunction if isGetterWithOptions { - handler = GetResourceWithOptions(getterWithOptions, exporter, reqScope, getOptionsInternalKind, getOptionsExternalKind, getSubpath, getSubpathKey) + handler = GetResourceWithOptions(getterWithOptions, reqScope) } else { handler = GetResource(getter, exporter, reqScope) } @@ -467,7 +467,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("read"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Writes(versionedObject) if isGetterWithOptions { @@ -492,7 +492,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("list"+namespaced+kind+strings.Title(subresource)). - Produces("application/json"). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedList). Writes(versionedList) if err := addObjectParams(ws, route, versionedListOptions); err != nil { @@ -524,7 +524,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("replace"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(versionedObject). Writes(versionedObject) @@ -541,7 +541,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Consumes(string(api.JSONPatchType), string(api.MergePatchType), string(api.StrategicMergePatchType)). Operation("patch"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(unversioned.Patch{}). Writes(versionedObject) @@ -563,7 +563,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("create"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(versionedObject). Writes(versionedObject) @@ -579,7 +579,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("delete"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Writes(versionedStatus). Returns(http.StatusOK, "OK", versionedStatus) if isGracefulDeleter { @@ -597,7 +597,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Operation("deletecollection"+namespaced+kind+strings.Title(subresource)). - Produces(append(storageMeta.ProducesMIMETypes(action.Verb), "application/json")...). + Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Writes(versionedStatus). Returns(http.StatusOK, "OK", versionedStatus) if err := addObjectParams(ws, route, versionedListOptions); err != nil { @@ -658,7 +658,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag doc = "connect " + method + " requests to " + subresource + " of " + kind } route := ws.Method(method).Path(action.Path). - To(ConnectResource(connecter, reqScope, admit, connectOptionsInternalKind, connectOptionsExternalKind, path, connectSubpath, connectSubpathKey)). + To(ConnectResource(connecter, reqScope, admit, path)). Filter(m). Doc(doc). Operation("connect" + strings.Title(strings.ToLower(method)) + namespaced + kind + strings.Title(subresource)). diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 2a3143f37cc..8f3c7aa898b 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -26,7 +26,6 @@ import ( "net/http" "path" rt "runtime" - "strconv" "strings" "time" @@ -36,7 +35,6 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apiserver/metrics" "k8s.io/kubernetes/pkg/healthz" "k8s.io/kubernetes/pkg/runtime" @@ -97,7 +95,9 @@ type APIGroupVersion struct { Mapper meta.RESTMapper - Codec runtime.Codec + Serializer runtime.NegotiatedSerializer + ParameterCodec runtime.ParameterCodec + Typer runtime.ObjectTyper Creater runtime.ObjectCreater Convertor runtime.ObjectConvertor @@ -126,7 +126,7 @@ func (g *APIGroupVersion) InstallREST(container *restful.Container) error { installer := g.newInstaller() ws := installer.NewWebService() apiResources, registrationErrors := installer.Install(ws) - AddSupportedResourcesWebService(ws, g.GroupVersion, apiResources) + AddSupportedResourcesWebService(g.Serializer, ws, g.GroupVersion, apiResources) container.Add(ws) return utilerrors.NewAggregate(registrationErrors) } @@ -150,7 +150,7 @@ func (g *APIGroupVersion) UpdateREST(container *restful.Container) error { return apierrors.NewInternalError(fmt.Errorf("unable to find an existing webservice for prefix %s", installer.prefix)) } apiResources, registrationErrors := installer.Install(ws) - AddSupportedResourcesWebService(ws, g.GroupVersion, apiResources) + AddSupportedResourcesWebService(g.Serializer, ws, g.GroupVersion, apiResources) return utilerrors.NewAggregate(registrationErrors) } @@ -194,12 +194,15 @@ func InstallLogsSupport(mux Mux) { mux.Handle("/logs/", http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/")))) } -func InstallRecoverHandler(container *restful.Container) { - container.RecoverHandler(logStackOnRecover) +// TODO: needs to perform response type negotiation, this is probably the wrong way to recover panics +func InstallRecoverHandler(s runtime.NegotiatedSerializer, container *restful.Container) { + container.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) { + logStackOnRecover(s, panicReason, httpWriter) + }) } //TODO: Unify with RecoverPanics? -func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) { +func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) { var buffer bytes.Buffer buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason)) for i := 2; ; i += 1 { @@ -211,125 +214,110 @@ func logStackOnRecover(panicReason interface{}, httpWriter http.ResponseWriter) } glog.Errorln(buffer.String()) - // TODO: make status unversioned or plumb enough of the request to deduce the requested API version - errorJSON(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), registered.GroupOrDie(api.GroupName).Codec, httpWriter) + headers := http.Header{} + if ct := w.Header().Get("Content-Type"); len(ct) > 0 { + headers.Set("Accept", ct) + } + errorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, w, &http.Request{Header: headers}) } -func InstallServiceErrorHandler(container *restful.Container, requestResolver *RequestInfoResolver, apiVersions []string) { +func InstallServiceErrorHandler(s runtime.NegotiatedSerializer, container *restful.Container, requestResolver *RequestInfoResolver, apiVersions []string) { container.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { - serviceErrorHandler(requestResolver, apiVersions, serviceErr, request, response) + serviceErrorHandler(s, requestResolver, apiVersions, serviceErr, request, response) }) } -func serviceErrorHandler(requestResolver *RequestInfoResolver, apiVersions []string, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { - requestInfo, err := requestResolver.GetRequestInfo(request.Request) - codec := registered.GroupOrDie(api.GroupName).Codec - if err == nil && requestInfo.APIVersion != "" { - // check if the api version is valid. - for _, version := range apiVersions { - if requestInfo.APIVersion == version { - // valid api version. - codec = runtime.CodecFor(api.Scheme, unversioned.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}) - break - } - } - } - - errorJSON(apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", "", 0, false), codec, response.ResponseWriter) +func serviceErrorHandler(s runtime.NegotiatedSerializer, requestResolver *RequestInfoResolver, apiVersions []string, serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) { + errorNegotiated(apierrors.NewGenericServerResponse(serviceErr.Code, "", api.Resource(""), "", "", 0, false), s, unversioned.GroupVersion{}, response.ResponseWriter, request.Request) } // Adds a service to return the supported api versions at the legacy /api. -func AddApiWebService(container *restful.Container, apiPrefix string, versions []string) { +func AddApiWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, versions []string) { // TODO: InstallREST should register each version automatically - versionHandler := APIVersionHandler(versions[:]...) + versionHandler := APIVersionHandler(s, versions[:]...) ws := new(restful.WebService) ws.Path(apiPrefix) ws.Doc("get available API versions") ws.Route(ws.GET("/").To(versionHandler). Doc("get available API versions"). Operation("getAPIVersions"). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON)) + Produces(s.SupportedMediaTypes()...). + Consumes(s.SupportedMediaTypes()...)) container.Add(ws) } // Adds a service to return the supported api versions at /apis. -func AddApisWebService(container *restful.Container, apiPrefix string, f func() []unversioned.APIGroup) { - rootAPIHandler := RootAPIHandler(f) +func AddApisWebService(s runtime.NegotiatedSerializer, container *restful.Container, apiPrefix string, f func() []unversioned.APIGroup) { + rootAPIHandler := RootAPIHandler(s, f) ws := new(restful.WebService) ws.Path(apiPrefix) ws.Doc("get available API versions") ws.Route(ws.GET("/").To(rootAPIHandler). Doc("get available API versions"). Operation("getAPIVersions"). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON)) + Produces(s.SupportedMediaTypes()...). + Consumes(s.SupportedMediaTypes()...)) container.Add(ws) } // Adds a service to return the supported versions, preferred version, and name // of a group. E.g., a such web service will be registered at /apis/extensions. -func AddGroupWebService(container *restful.Container, path string, group unversioned.APIGroup) { - groupHandler := GroupHandler(group) +func AddGroupWebService(s runtime.NegotiatedSerializer, container *restful.Container, path string, group unversioned.APIGroup) { + groupHandler := GroupHandler(s, group) ws := new(restful.WebService) ws.Path(path) ws.Doc("get information of a group") ws.Route(ws.GET("/").To(groupHandler). Doc("get information of a group"). Operation("getAPIGroup"). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON)) + Produces(s.SupportedMediaTypes()...). + Consumes(s.SupportedMediaTypes()...)) container.Add(ws) } // Adds a service to return the supported resources, E.g., a such web service // will be registered at /apis/extensions/v1. -func AddSupportedResourcesWebService(ws *restful.WebService, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) { - resourceHandler := SupportedResourcesHandler(groupVersion, apiResources) +func AddSupportedResourcesWebService(s runtime.NegotiatedSerializer, ws *restful.WebService, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) { + resourceHandler := SupportedResourcesHandler(s, groupVersion, apiResources) ws.Route(ws.GET("/").To(resourceHandler). Doc("get available resources"). Operation("getAPIResources"). - Produces(restful.MIME_JSON). - Consumes(restful.MIME_JSON)) + Produces(s.SupportedMediaTypes()...). + Consumes(s.SupportedMediaTypes()...)) } // handleVersion writes the server's version information. func handleVersion(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods writeRawJSON(http.StatusOK, version.Get(), resp.ResponseWriter) } // APIVersionHandler returns a handler which will list the provided versions as available. -func APIVersionHandler(versions ...string) restful.RouteFunction { +func APIVersionHandler(s runtime.NegotiatedSerializer, versions ...string) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods - writeJSON(http.StatusOK, api.Codec, &unversioned.APIVersions{Versions: versions}, resp.ResponseWriter, true) + writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIVersions{Versions: versions}) } } // RootAPIHandler returns a handler which will list the provided groups and versions as available. -func RootAPIHandler(f func() []unversioned.APIGroup) restful.RouteFunction { +func RootAPIHandler(s runtime.NegotiatedSerializer, f func() []unversioned.APIGroup) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods - writeJSON(http.StatusOK, api.Codec, &unversioned.APIGroupList{Groups: f()}, resp.ResponseWriter, true) + writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIGroupList{Groups: f()}) } } // GroupHandler returns a handler which will return the api.GroupAndVersion of // the group. -func GroupHandler(group unversioned.APIGroup) restful.RouteFunction { +func GroupHandler(s runtime.NegotiatedSerializer, group unversioned.APIGroup) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods - writeJSON(http.StatusOK, api.Codec, &group, resp.ResponseWriter, true) + writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &group) } } // SupportedResourcesHandler returns a handler which will list the provided resources as available. -func SupportedResourcesHandler(groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) restful.RouteFunction { +func SupportedResourcesHandler(s runtime.NegotiatedSerializer, groupVersion unversioned.GroupVersion, apiResources []unversioned.APIResource) restful.RouteFunction { return func(req *restful.Request, resp *restful.Response) { - // TODO: use restful's Response methods - writeJSON(http.StatusOK, api.Codec, &unversioned.APIResourceList{GroupVersion: groupVersion.String(), APIResources: apiResources}, resp.ResponseWriter, true) + writeNegotiated(s, unversioned.GroupVersion{}, resp.ResponseWriter, req.Request, http.StatusOK, &unversioned.APIResourceList{GroupVersion: groupVersion.String(), APIResources: apiResources}) } } @@ -338,11 +326,11 @@ func SupportedResourcesHandler(groupVersion unversioned.GroupVersion, apiResourc // response. The Accept header and current API version will be passed in, and the output will be copied // directly to the response body. If content type is returned it is used, otherwise the content type will // be "application/octet-stream". All other objects are sent to standard JSON serialization. -func write(statusCode int, groupVersion unversioned.GroupVersion, codec runtime.Codec, object runtime.Object, w http.ResponseWriter, req *http.Request) { +func write(statusCode int, gv unversioned.GroupVersion, s runtime.NegotiatedSerializer, object runtime.Object, w http.ResponseWriter, req *http.Request) { if stream, ok := object.(rest.ResourceStreamer); ok { - out, flush, contentType, err := stream.InputStream(groupVersion.String(), req.Header.Get("Accept")) + out, flush, contentType, err := stream.InputStream(gv.String(), req.Header.Get("Accept")) if err != nil { - errorJSONFatal(err, codec, w) + errorNegotiated(err, s, gv, w, req) return } if out == nil { @@ -372,64 +360,38 @@ func write(statusCode int, groupVersion unversioned.GroupVersion, codec runtime. io.Copy(writer, out) return } - writeJSON(statusCode, codec, object, w, isPrettyPrint(req)) + writeNegotiated(s, gv, w, req, statusCode, object) } -func isPrettyPrint(req *http.Request) bool { - pp := req.URL.Query().Get("pretty") - if len(pp) > 0 { - pretty, _ := strconv.ParseBool(pp) - return pretty +// writeNegotiated renders an object in the content type negotiated by the client +func writeNegotiated(s runtime.NegotiatedSerializer, gv unversioned.GroupVersion, w http.ResponseWriter, req *http.Request, statusCode int, object runtime.Object) { + serializer, contentType, err := negotiateOutputSerializer(req, s) + if err != nil { + status := errToAPIStatus(err) + writeRawJSON(int(status.Code), status, w) + return } - userAgent := req.UserAgent() - // This covers basic all browers and cli http tools - if strings.HasPrefix(userAgent, "curl") || strings.HasPrefix(userAgent, "Wget") || strings.HasPrefix(userAgent, "Mozilla/5.0") { - return true - } - return false -} -// writeJSON renders an object as JSON to the response. -func writeJSON(statusCode int, codec runtime.Codec, object runtime.Object, w http.ResponseWriter, pretty bool) { - w.Header().Set("Content-Type", "application/json") - // We send the status code before we encode the object, so if we error, the status code stays but there will - // still be an error object. This seems ok, the alternative is to validate the object before - // encoding, but this really should never happen, so it's wasted compute for every API request. + w.Header().Set("Content-Type", contentType) w.WriteHeader(statusCode) - if pretty { - prettyJSON(codec, object, w) - return - } - err := codec.EncodeToStream(object, w) - if err != nil { - errorJSONFatal(err, codec, w) + + encoder := s.EncoderForVersion(serializer, gv) + if err := encoder.EncodeToStream(object, w); err != nil { + errorJSONFatal(err, encoder, w) } } -func prettyJSON(codec runtime.Codec, object runtime.Object, w http.ResponseWriter) { - formatted := &bytes.Buffer{} - output, err := runtime.Encode(codec, object) - if err != nil { - errorJSONFatal(err, codec, w) - } - if err := json.Indent(formatted, output, "", " "); err != nil { - errorJSONFatal(err, codec, w) - return - } - w.Write(formatted.Bytes()) -} - -// errorJSON renders an error to the response. Returns the HTTP status code of the error. -func errorJSON(err error, codec runtime.Codec, w http.ResponseWriter) int { +// errorNegotiated renders an error to the response. Returns the HTTP status code of the error. +func errorNegotiated(err error, s runtime.NegotiatedSerializer, gv unversioned.GroupVersion, w http.ResponseWriter, req *http.Request) int { status := errToAPIStatus(err) code := int(status.Code) - writeJSON(code, codec, status, w, true) + writeNegotiated(s, gv, w, req, code, status) return code } // errorJSONFatal renders an error to the response, and if codec fails will render plaintext. // Returns the HTTP status code of the error. -func errorJSONFatal(err error, codec runtime.Codec, w http.ResponseWriter) int { +func errorJSONFatal(err error, codec runtime.Encoder, w http.ResponseWriter) int { util.HandleError(fmt.Errorf("apiserver was unable to write a JSON response: %v", err)) status := errToAPIStatus(err) code := int(status.Code) diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index 503c6dc0253..6141edd47e8 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -58,18 +58,21 @@ func convert(obj runtime.Object) (runtime.Object, error) { // This creates fake API versions, similar to api/latest.go. var testAPIGroup = "test.group" -var testInternalGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: ""} +var testInternalGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: runtime.APIVersionInternal} var testGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version"} var newGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version2"} var prefix = "apis" var grouplessGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"} +var grouplessInternalGroupVersion = unversioned.GroupVersion{Group: "", Version: runtime.APIVersionInternal} var grouplessPrefix = "api" -var grouplessCodec = runtime.CodecFor(api.Scheme, grouplessGroupVersion) var groupVersions = []unversioned.GroupVersion{grouplessGroupVersion, testGroupVersion, newGroupVersion} -var codec = runtime.CodecFor(api.Scheme, testGroupVersion) -var newCodec = runtime.CodecFor(api.Scheme, newGroupVersion) + +var codec = api.Codecs.LegacyCodec(groupVersions...) +var grouplessCodec = api.Codecs.LegacyCodec(grouplessGroupVersion) +var testCodec = api.Codecs.LegacyCodec(testGroupVersion) +var newCodec = api.Codecs.LegacyCodec(newGroupVersion) var accessor = meta.NewAccessor() var versioner runtime.ResourceVersioner = accessor @@ -82,19 +85,16 @@ func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, e switch version { case testGroupVersion: return &meta.VersionInterfaces{ - Codec: codec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil case newGroupVersion: return &meta.VersionInterfaces{ - Codec: newCodec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil case grouplessGroupVersion: return &meta.VersionInterfaces{ - Codec: grouplessCodec, ObjectConvertor: api.Scheme, MetadataAccessor: accessor, }, nil @@ -117,10 +117,13 @@ func addGrouplessTypes() { ResourceVersion string `json:"resourceVersion,omitempty"` TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"` } - api.Scheme.AddKnownTypes( - grouplessGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, - &ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) + api.Scheme.AddKnownTypes(grouplessGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{}, + &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) api.Scheme.AddKnownTypes(grouplessGroupVersion, &api.Pod{}) + api.Scheme.AddKnownTypes(grouplessInternalGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{}, + &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) } func addTestTypes() { @@ -133,11 +136,13 @@ func addTestTypes() { ResourceVersion string `json:"resourceVersion,omitempty"` TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"` } - api.Scheme.AddKnownTypes( - testGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, - &ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}, - &unversioned.ExportOptions{}) + api.Scheme.AddKnownTypes(testGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{}, + &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) api.Scheme.AddKnownTypes(testGroupVersion, &api.Pod{}) + api.Scheme.AddKnownTypes(testInternalGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{}, + &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) } func addNewTestTypes() { @@ -150,21 +155,15 @@ func addNewTestTypes() { ResourceVersion string `json:"resourceVersion,omitempty"` TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"` } - api.Scheme.AddKnownTypes( - newGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, - &ListOptions{}, &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}, - &unversioned.ExportOptions{}) + api.Scheme.AddKnownTypes(newGroupVersion, + &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{}, + &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) } func init() { // Certain API objects are returned regardless of the contents of storage: // api.Status is returned in errors - // "internal" version - api.Scheme.AddKnownTypes( - testInternalGroupVersion, &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &unversioned.Status{}, - &api.ListOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{}) - api.Scheme.AddInternalGroupVersion(testInternalGroupVersion) addGrouplessTypes() addTestTypes() addNewTestTypes() @@ -253,6 +252,8 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. Linker: selfLinker, Mapper: namespaceMapper, + ParameterCodec: api.ParameterCodec, + Admit: admissionControl, Context: requestContextMapper, } @@ -263,7 +264,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + grouplessPrefix group.GroupVersion = grouplessGroupVersion group.OptionsExternalVersion = &grouplessGroupVersion - group.Codec = grouplessCodec + group.Serializer = api.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -275,7 +276,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + prefix group.GroupVersion = testGroupVersion group.OptionsExternalVersion = &testGroupVersion - group.Codec = codec + group.Serializer = api.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -287,7 +288,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission. group.Root = "/" + prefix group.GroupVersion = newGroupVersion group.OptionsExternalVersion = &newGroupVersion - group.Codec = newCodec + group.Serializer = api.Codecs if err := (&group).InstallREST(container); err != nil { panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err)) } @@ -430,7 +431,11 @@ func (storage *SimpleRESTStorage) Get(ctx api.Context, id string) (runtime.Objec if id == "binary" { return storage.stream, storage.errors["get"] } - return api.Scheme.CopyOrDie(&storage.item), storage.errors["get"] + copied, err := api.Scheme.Copy(&storage.item) + if err != nil { + panic(err) + } + return copied, storage.errors["get"] } func (storage *SimpleRESTStorage) checkContext(ctx api.Context) { @@ -645,7 +650,11 @@ func (storage *SimpleTypedStorage) New() runtime.Object { func (storage *SimpleTypedStorage) Get(ctx api.Context, id string) (runtime.Object, error) { storage.checkContext(ctx) - return api.Scheme.CopyOrDie(storage.item), storage.errors["get"] + copied, err := api.Scheme.Copy(storage.item) + if err != nil { + panic(err) + } + return copied, storage.errors["get"] } func (storage *SimpleTypedStorage) checkContext(ctx api.Context) { @@ -653,13 +662,16 @@ func (storage *SimpleTypedStorage) checkContext(ctx api.Context) { } func extractBody(response *http.Response, object runtime.Object) (string, error) { + return extractBodyDecoder(response, object, codec) +} + +func extractBodyDecoder(response *http.Response, object runtime.Object, decoder runtime.Decoder) (string, error) { defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { return string(body), err } - err = runtime.DecodeInto(codec, body, object) - return string(body), err + return string(body), runtime.DecodeInto(decoder, body, object) } func TestNotFound(t *testing.T) { @@ -1196,7 +1208,11 @@ func TestMetadata(t *testing.T) { matches[s] = i + 1 } } - if matches["text/plain,application/json"] == 0 || matches["application/json"] == 0 || matches["*/*"] == 0 || len(matches) != 3 { + if matches["text/plain,application/json,application/yaml"] == 0 || + matches["application/json,application/yaml"] == 0 || + matches["application/json"] == 0 || + matches["*/*"] == 0 || + len(matches) != 4 { t.Errorf("unexpected mime types: %v", matches) } } @@ -2060,7 +2076,7 @@ func TestUpdate(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2098,7 +2114,7 @@ func TestUpdateInvokesAdmissionControl(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2128,7 +2144,7 @@ func TestUpdateRequiresMatchingName(t *testing.T) { item := &apiservertesting.Simple{ Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2161,7 +2177,7 @@ func TestUpdateAllowsMissingNamespace(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2200,7 +2216,7 @@ func TestUpdateAllowsMismatchedNamespaceOnError(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2238,7 +2254,7 @@ func TestUpdatePreventsMismatchedNamespace(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { // The following cases will fail, so die now t.Fatalf("unexpected error: %v", err) @@ -2274,7 +2290,7 @@ func TestUpdateMissing(t *testing.T) { }, Other: "bar", } - body, err := runtime.Encode(codec, item) + body, err := runtime.Encode(testCodec, item) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2304,7 +2320,7 @@ func TestCreateNotFound(t *testing.T) { client := http.Client{} simple := &apiservertesting.Simple{Other: "foo"} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2331,7 +2347,7 @@ func TestCreateChecksDecode(t *testing.T) { client := http.Client{} simple := &api.Pod{} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(codec, simple, testGroupVersion) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2373,7 +2389,9 @@ func TestUpdateREST(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Codec: newCodec, + + Serializer: api.Codecs, + ParameterCodec: api.ParameterCodec, } } @@ -2455,7 +2473,9 @@ func TestParentResourceIsRequired(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Codec: newCodec, + + Serializer: api.Codecs, + ParameterCodec: api.ParameterCodec, } container := restful.NewContainer() if err := group.InstallREST(container); err == nil { @@ -2484,7 +2504,9 @@ func TestParentResourceIsRequired(t *testing.T) { GroupVersion: newGroupVersion, OptionsExternalVersion: &newGroupVersion, - Codec: newCodec, + + Serializer: api.Codecs, + ParameterCodec: api.ParameterCodec, } container = restful.NewContainer() if err := group.InstallREST(container); err != nil { @@ -2522,7 +2544,7 @@ func TestCreateWithName(t *testing.T) { client := http.Client{} simple := &apiservertesting.Simple{Other: "foo"} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2550,7 +2572,7 @@ func TestUpdateChecksDecode(t *testing.T) { client := http.Client{} simple := &api.Pod{} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(codec, simple, testGroupVersion) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2563,7 +2585,7 @@ func TestUpdateChecksDecode(t *testing.T) { t.Errorf("unexpected error: %v", err) } if response.StatusCode != http.StatusBadRequest { - t.Errorf("Unexpected response %#v", response) + t.Errorf("Unexpected response %#v\n%s", response, readBodyOrDie(response.Body)) } b, err := ioutil.ReadAll(response.Body) if err != nil { @@ -2627,7 +2649,7 @@ func TestCreate(t *testing.T) { simple := &apiservertesting.Simple{ Other: "bar", } - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2651,7 +2673,7 @@ func TestCreate(t *testing.T) { var itemOut apiservertesting.Simple body, err := extractBody(response, &itemOut) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Errorf("unexpected error: %v %#v", err, response) } if !reflect.DeepEqual(&itemOut, simple) { @@ -2665,6 +2687,74 @@ func TestCreate(t *testing.T) { } } +func TestCreateYAML(t *testing.T) { + storage := SimpleRESTStorage{ + injectedFunction: func(obj runtime.Object) (runtime.Object, error) { + time.Sleep(5 * time.Millisecond) + return obj, nil + }, + } + selfLinker := &setTestSelfLinker{ + t: t, + name: "bar", + namespace: "default", + expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo/bar", + } + handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker) + server := httptest.NewServer(handler) + defer server.Close() + client := http.Client{} + + // yaml encoder + simple := &apiservertesting.Simple{ + Other: "bar", + } + serializer, ok := api.Codecs.SerializerForMediaType("application/yaml", nil) + if !ok { + t.Fatal("No yaml serializer") + } + encoder := api.Codecs.EncoderForVersion(serializer, testGroupVersion) + decoder := api.Codecs.DecoderToVersion(serializer, testInternalGroupVersion) + + data, err := runtime.Encode(encoder, simple) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + request.Header.Set("Accept", "application/yaml, application/json") + request.Header.Set("Content-Type", "application/yaml") + + wg := sync.WaitGroup{} + wg.Add(1) + var response *http.Response + go func() { + response, err = client.Do(request) + wg.Done() + }() + wg.Wait() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + var itemOut apiservertesting.Simple + body, err := extractBodyDecoder(response, &itemOut, decoder) + if err != nil { + t.Fatalf("unexpected error: %v %#v", err, response) + } + + if !reflect.DeepEqual(&itemOut, simple) { + t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body)) + } + if response.StatusCode != http.StatusCreated { + t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response) + } + if !selfLinker.called { + t.Errorf("Never set self link") + } +} func TestCreateInNamespace(t *testing.T) { storage := SimpleRESTStorage{ injectedFunction: func(obj runtime.Object) (runtime.Object, error) { @@ -2687,13 +2777,13 @@ func TestCreateInNamespace(t *testing.T) { simple := &apiservertesting.Simple{ Other: "bar", } - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/other/foo", bytes.NewBuffer(data)) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } wg := sync.WaitGroup{} @@ -2705,13 +2795,13 @@ func TestCreateInNamespace(t *testing.T) { }() wg.Wait() if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } var itemOut apiservertesting.Simple body, err := extractBody(response, &itemOut) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v\n%s", err, data) } if !reflect.DeepEqual(&itemOut, simple) { @@ -2747,7 +2837,7 @@ func TestCreateInvokesAdmissionControl(t *testing.T) { simple := &apiservertesting.Simple{ Other: "bar", } - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2822,7 +2912,7 @@ func (obj *UnregisteredAPIObject) GetObjectKind() unversioned.ObjectKind { func TestWriteJSONDecodeError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - writeJSON(http.StatusOK, codec, &UnregisteredAPIObject{"Undecodable"}, w, false) + writeNegotiated(api.Codecs, newGroupVersion, w, req, http.StatusOK, &UnregisteredAPIObject{"Undecodable"}) })) // TODO: Uncomment when fix #19254 // defer server.Close() @@ -2833,7 +2923,7 @@ func TestWriteJSONDecodeError(t *testing.T) { if status.Reason != unversioned.StatusReasonUnknown { t.Errorf("unexpected reason %#v", status) } - if !strings.Contains(status.Message, "type apiserver.UnregisteredAPIObject is not registered") { + if !strings.Contains(status.Message, "no kind is registered for the type apiserver.UnregisteredAPIObject") { t.Errorf("unexpected message %#v", status) } } @@ -2881,7 +2971,7 @@ func TestCreateTimeout(t *testing.T) { // defer server.Close() simple := &apiservertesting.Simple{Other: "foo"} - data, err := runtime.Encode(codec, simple) + data, err := runtime.Encode(testCodec, simple) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -2993,7 +3083,7 @@ func TestCreateChecksAPIVersion(t *testing.T) { b, err := ioutil.ReadAll(response.Body) if err != nil { t.Errorf("unexpected error: %v", err) - } else if !strings.Contains(string(b), "does not match the specified apiVersion") { + } else if !strings.Contains(string(b), "does not match the expected API version") { t.Errorf("unexpected response: %s", string(b)) } } @@ -3044,15 +3134,15 @@ func TestUpdateChecksAPIVersion(t *testing.T) { simple := &apiservertesting.Simple{ObjectMeta: api.ObjectMeta{Name: "bar"}} data, err := runtime.Encode(newCodec, simple) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/bar", bytes.NewBuffer(data)) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } response, err := client.Do(request) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } if response.StatusCode != http.StatusBadRequest { t.Errorf("Unexpected response %#v", response) @@ -3060,7 +3150,15 @@ func TestUpdateChecksAPIVersion(t *testing.T) { b, err := ioutil.ReadAll(response.Body) if err != nil { t.Errorf("unexpected error: %v", err) - } else if !strings.Contains(string(b), "does not match the specified apiVersion") { + } else if !strings.Contains(string(b), "does not match the expected API version") { t.Errorf("unexpected response: %s", string(b)) } } + +func readBodyOrDie(r io.Reader) []byte { + body, err := ioutil.ReadAll(r) + if err != nil { + panic(err) + } + return body +} diff --git a/pkg/apiserver/errors.go b/pkg/apiserver/errors.go index d3d485e7d09..c7c5e9a90f1 100644 --- a/pkg/apiserver/errors.go +++ b/pkg/apiserver/errors.go @@ -19,6 +19,7 @@ package apiserver import ( "fmt" "net/http" + "strings" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/storage" @@ -105,3 +106,41 @@ func IsAPIPrefixNotFound(err error) bool { _, ok := err.(*errAPIPrefixNotFound) return ok } + +// errNotAcceptable indicates Accept negotiation has failed +// TODO: move to api/errors if other code needs to return this +type errNotAcceptable struct { + accepted []string +} + +func (e errNotAcceptable) Error() string { + return fmt.Sprintf("only the following media types are accepted: %v", strings.Join(e.accepted, ", ")) +} + +func (e errNotAcceptable) Status() unversioned.Status { + return unversioned.Status{ + Status: unversioned.StatusFailure, + Code: http.StatusNotAcceptable, + Reason: unversioned.StatusReason("NotAcceptable"), + Message: e.Error(), + } +} + +// errNotAcceptable indicates Content-Type is not recognized +// TODO: move to api/errors if other code needs to return this +type errUnsupportedMediaType struct { + accepted []string +} + +func (e errUnsupportedMediaType) Error() string { + return fmt.Sprintf("the body of the request was in an unknown format - accepted media types include: %v", strings.Join(e.accepted, ", ")) +} + +func (e errUnsupportedMediaType) Status() unversioned.Status { + return unversioned.Status{ + Status: unversioned.StatusFailure, + Code: http.StatusUnsupportedMediaType, + Reason: unversioned.StatusReason("UnsupportedMediaType"), + Message: e.Error(), + } +} diff --git a/pkg/apiserver/negotiate.go b/pkg/apiserver/negotiate.go new file mode 100644 index 00000000000..1457addbfc2 --- /dev/null +++ b/pkg/apiserver/negotiate.go @@ -0,0 +1,116 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apiserver + +import ( + "mime" + "net/http" + "strconv" + "strings" + + "bitbucket.org/ww/goautoneg" + + "k8s.io/kubernetes/pkg/runtime" +) + +func negotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.Serializer, string, error) { + acceptHeader := req.Header.Get("Accept") + supported := ns.SupportedMediaTypes() + if len(acceptHeader) == 0 && len(supported) > 0 { + acceptHeader = supported[0] + } + accept, ok := negotiate(acceptHeader, supported) + if !ok { + return nil, "", errNotAcceptable{supported} + } + + pretty := isPrettyPrint(req) + if _, ok := accept.Params["pretty"]; !ok && pretty { + accept.Params["pretty"] = "1" + } + mediaType := accept.Type + if len(accept.SubType) > 0 { + mediaType += "/" + accept.SubType + } + if s, ok := ns.SerializerForMediaType(mediaType, accept.Params); ok { + return s, mediaType, nil + } + + return nil, "", errNotAcceptable{supported} +} + +func negotiateInputSerializer(req *http.Request, s runtime.NegotiatedSerializer) (runtime.Serializer, error) { + supported := s.SupportedMediaTypes() + mediaType := req.Header.Get("Content-Type") + if len(mediaType) == 0 { + mediaType = supported[0] + } + mediaType, options, err := mime.ParseMediaType(mediaType) + if err != nil { + return nil, errUnsupportedMediaType{supported} + } + out, ok := s.SerializerForMediaType(mediaType, options) + if !ok { + return nil, errUnsupportedMediaType{supported} + } + return out, nil +} + +// isPrettyPrint returns true if the "pretty" query parameter is true or if the User-Agent +// matches known "human" clients. +func isPrettyPrint(req *http.Request) bool { + // DEPRECATED: should be part of the content type + if req.URL != nil { + pp := req.URL.Query().Get("pretty") + if len(pp) > 0 { + pretty, _ := strconv.ParseBool(pp) + return pretty + } + } + userAgent := req.UserAgent() + // This covers basic all browers and cli http tools + if strings.HasPrefix(userAgent, "curl") || strings.HasPrefix(userAgent, "Wget") || strings.HasPrefix(userAgent, "Mozilla/5.0") { + return true + } + return false +} + +// negotiate the most appropriate content type given the accept header and a list of +// alternatives. +func negotiate(header string, alternatives []string) (goautoneg.Accept, bool) { + alternates := make([][]string, 0, len(alternatives)) + for _, alternate := range alternatives { + alternates = append(alternates, strings.SplitN(alternate, "/", 2)) + } + for _, clause := range goautoneg.ParseAccept(header) { + for _, alternate := range alternates { + if clause.Type == alternate[0] && clause.SubType == alternate[1] { + return clause, true + } + if clause.Type == alternate[0] && clause.SubType == "*" { + clause.SubType = alternate[1] + return clause, true + } + if clause.Type == "*" && clause.SubType == "*" { + clause.Type = alternate[0] + clause.SubType = alternate[1] + return clause, true + } + } + } + return goautoneg.Accept{}, false +} diff --git a/pkg/apiserver/negotiate_test.go b/pkg/apiserver/negotiate_test.go new file mode 100644 index 00000000000..8e59d6d69cc --- /dev/null +++ b/pkg/apiserver/negotiate_test.go @@ -0,0 +1,252 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apiserver + +import ( + "net/http" + "net/url" + "reflect" + "testing" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" +) + +type fakeNegotiater struct { + serializer runtime.Serializer + types []string + mediaType string + options map[string]string +} + +func (n *fakeNegotiater) SupportedMediaTypes() []string { + return n.types +} + +func (n *fakeNegotiater) SerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, bool) { + n.mediaType = mediaType + if len(options) > 0 { + n.options = options + } + return n.serializer, n.serializer != nil +} + +func (n *fakeNegotiater) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder { + return n.serializer +} + +func (n *fakeNegotiater) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder { + return n.serializer +} + +var fakeCodec = runtime.NewCodec(runtime.NoopEncoder{}, runtime.NoopDecoder{}) + +func TestNegotiate(t *testing.T) { + testCases := []struct { + accept string + req *http.Request + ns *fakeNegotiater + serializer runtime.Serializer + contentType string + params map[string]string + errFn func(error) bool + }{ + // pick a default + { + req: &http.Request{}, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "*/*", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "application/*", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "application/json", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + }, + { + accept: "application/json", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json", "application/protobuf"}}, + serializer: fakeCodec, + }, + { + accept: "application/protobuf", + contentType: "application/protobuf", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json", "application/protobuf"}}, + serializer: fakeCodec, + }, + { + accept: "application/json; pretty=1", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + { + accept: "unrecognized/stuff,application/json; pretty=1", + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + + // query param triggers pretty + { + req: &http.Request{ + Header: http.Header{"Accept": []string{"application/json"}}, + URL: &url.URL{RawQuery: "pretty=1"}, + }, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + + // certain user agents trigger pretty + { + req: &http.Request{ + Header: http.Header{ + "Accept": []string{"application/json"}, + "User-Agent": []string{"curl"}, + }, + }, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + { + req: &http.Request{ + Header: http.Header{ + "Accept": []string{"application/json"}, + "User-Agent": []string{"Wget"}, + }, + }, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + { + req: &http.Request{ + Header: http.Header{ + "Accept": []string{"application/json"}, + "User-Agent": []string{"Mozilla/5.0"}, + }, + }, + contentType: "application/json", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application/json"}}, + serializer: fakeCodec, + params: map[string]string{"pretty": "1"}, + }, + + // "application" is not a valid media type, so the server will reject the response during + // negotiation (the server, in error, has specified an invalid media type) + { + accept: "application", + ns: &fakeNegotiater{serializer: fakeCodec, types: []string{"application"}}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: application" + }, + }, + { + ns: &fakeNegotiater{types: []string{"a/b/c"}}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: a/b/c" + }, + }, + { + ns: &fakeNegotiater{}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: " + }, + }, + { + accept: "*/*", + ns: &fakeNegotiater{}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: " + }, + }, + { + accept: "application/json", + ns: &fakeNegotiater{types: []string{"application/json"}}, + errFn: func(err error) bool { + return err.Error() == "only the following media types are accepted: application/json" + }, + }, + } + + for i, test := range testCases { + req := test.req + if req == nil { + req = &http.Request{Header: http.Header{}} + req.Header.Set("Accept", test.accept) + } + s, contentType, err := negotiateOutputSerializer(req, test.ns) + switch { + case err == nil && test.errFn != nil: + t.Errorf("%d: failed: expected error", i) + continue + case err != nil && test.errFn == nil: + t.Errorf("%d: failed: %v", i, err) + continue + case err != nil: + if !test.errFn(err) { + t.Errorf("%d: failed: %v", i, err) + } + status, ok := err.(statusError) + if !ok { + t.Errorf("%d: failed, error should be statusError: %v", i, err) + continue + } + if status.Status().Status != unversioned.StatusFailure || status.Status().Code != http.StatusNotAcceptable { + t.Errorf("%d: failed: %v", i, err) + continue + } + continue + } + if test.contentType != contentType { + t.Errorf("%d: unexpected %s %s", i, test.contentType, contentType) + } + if s != test.serializer { + t.Errorf("%d: unexpected %s %s", i, test.serializer, s) + } + if !reflect.DeepEqual(test.params, test.ns.options) { + t.Errorf("%d: unexpected %#v %#v", i, test.params, test.ns.options) + } + } +} diff --git a/pkg/apiserver/proxy.go b/pkg/apiserver/proxy.go index 32474daaa9c..d640cb957f6 100644 --- a/pkg/apiserver/proxy.go +++ b/pkg/apiserver/proxy.go @@ -29,6 +29,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/rest" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apiserver/metrics" "k8s.io/kubernetes/pkg/httplog" "k8s.io/kubernetes/pkg/runtime" @@ -44,7 +45,7 @@ import ( type ProxyHandler struct { prefix string storage map[string]rest.Storage - codec runtime.Codec + serializer runtime.NegotiatedSerializer context api.RequestContextMapper requestInfoResolver *RequestInfoResolver } @@ -98,20 +99,19 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } apiResource = resource + gv := unversioned.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion} + redirector, ok := storage.(rest.Redirector) if !ok { httplog.LogOf(req, w).Addf("'%v' is not a redirector", resource) - httpCode = errorJSON(errors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.codec, w) + httpCode = errorNegotiated(errors.NewMethodNotSupported(api.Resource(resource), "proxy"), r.serializer, gv, w, req) return } location, roundTripper, err := redirector.ResourceLocation(ctx, id) if err != nil { httplog.LogOf(req, w).Addf("Error getting ResourceLocation: %v", err) - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) - httpCode = code + httpCode = errorNegotiated(err, r.serializer, gv, w, req) return } if location == nil { @@ -144,11 +144,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { newReq, err := http.NewRequest(req.Method, location.String(), req.Body) if err != nil { - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) - notFound(w, req) - httpCode = code + httpCode = errorNegotiated(err, r.serializer, gv, w, req) return } httpCode = http.StatusOK @@ -161,7 +157,7 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // TODO convert this entire proxy to an UpgradeAwareProxy similar to // https://github.com/openshift/origin/blob/master/pkg/util/httpproxy/upgradeawareproxy.go. // That proxy needs to be modified to support multiple backends, not just 1. - if r.tryUpgrade(w, req, newReq, location, roundTripper) { + if r.tryUpgrade(w, req, newReq, location, roundTripper, gv) { return } @@ -210,15 +206,13 @@ func (r *ProxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // tryUpgrade returns true if the request was handled. -func (r *ProxyHandler) tryUpgrade(w http.ResponseWriter, req, newReq *http.Request, location *url.URL, transport http.RoundTripper) bool { +func (r *ProxyHandler) tryUpgrade(w http.ResponseWriter, req, newReq *http.Request, location *url.URL, transport http.RoundTripper, gv unversioned.GroupVersion) bool { if !httpstream.IsUpgradeRequest(req) { return false } backendConn, err := proxyutil.DialURL(location, transport) if err != nil { - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) + errorNegotiated(err, r.serializer, gv, w, req) return true } defer backendConn.Close() @@ -228,17 +222,13 @@ func (r *ProxyHandler) tryUpgrade(w http.ResponseWriter, req, newReq *http.Reque // hijack, just for reference... requestHijackedConn, _, err := w.(http.Hijacker).Hijack() if err != nil { - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) + errorNegotiated(err, r.serializer, gv, w, req) return true } defer requestHijackedConn.Close() if err = newReq.Write(backendConn); err != nil { - status := errToAPIStatus(err) - code := int(status.Code) - writeJSON(code, r.codec, status, w, true) + errorNegotiated(err, r.serializer, gv, w, req) return true } diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 41032066af1..43674b933ef 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -32,7 +32,6 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/rest" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" @@ -70,7 +69,8 @@ type ScopeNamer interface { type RequestScope struct { Namer ScopeNamer ContextFunc - runtime.Codec + Serializer runtime.NegotiatedSerializer + runtime.ParameterCodec Creater runtime.ObjectCreater Convertor runtime.ObjectConvertor @@ -79,6 +79,10 @@ type RequestScope struct { Subresource string } +func (scope *RequestScope) err(err error, req *restful.Request, res *restful.Response) { + errorNegotiated(err, scope.Serializer, scope.Kind.GroupVersion(), res.ResponseWriter, req.Request) +} + // getterFunc performs a get request with the given context and object name. The request // may be used to deserialize an options object to pass to the getter. type getterFunc func(ctx api.Context, name string, req *restful.Request) (runtime.Object, error) @@ -93,7 +97,7 @@ func getResourceHandler(scope RequestScope, getter getterFunc) restful.RouteFunc w := res.ResponseWriter namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) @@ -101,14 +105,14 @@ func getResourceHandler(scope RequestScope, getter getterFunc) restful.RouteFunc result, err := getter(ctx, name, req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -119,44 +123,41 @@ func GetResource(r rest.Getter, e rest.Exporter, scope RequestScope) restful.Rou // For performance tracking purposes. trace := util.NewTrace("Get " + req.Request.URL.Path) defer trace.LogIfLong(250 * time.Millisecond) - opts := v1.ExportOptions{} - if err := scope.Codec.DecodeParametersInto(req.Request.URL.Query(), &opts); err != nil { - return nil, err - } - internalOpts := unversioned.ExportOptions{} - scope.Convertor.Convert(&opts, &internalOpts) - if internalOpts.Export { - if e == nil { - return nil, errors.NewBadRequest("export unsupported") + + // check for export + if values := req.Request.URL.Query(); len(values) > 0 { + // TODO: this is internal version, not unversioned + exports := unversioned.ExportOptions{} + if err := scope.ParameterCodec.DecodeParameters(values, unversioned.GroupVersion{Version: "v1"}, &exports); err != nil { + return nil, err + } + if exports.Export { + if e == nil { + return nil, errors.NewBadRequest(fmt.Sprintf("export of %q is not supported", scope.Resource.Resource)) + } + return e.Export(ctx, name, exports) } - return e.Export(ctx, name, internalOpts) } + return r.Get(ctx, name) }) } // GetResourceWithOptions returns a function that handles retrieving a single resource from a rest.Storage object. -func GetResourceWithOptions(r rest.GetterWithOptions, e rest.Exporter, scope RequestScope, internalKind, externalKind unversioned.GroupVersionKind, subpath bool, subpathKey string) restful.RouteFunction { +func GetResourceWithOptions(r rest.GetterWithOptions, scope RequestScope) restful.RouteFunction { return getResourceHandler(scope, func(ctx api.Context, name string, req *restful.Request) (runtime.Object, error) { - opts, err := getRequestOptions(req, scope, internalKind, externalKind, subpath, subpathKey) - if err != nil { + opts, subpath, subpathKey := r.NewGetOptions() + if err := getRequestOptions(req, scope, opts, subpath, subpathKey); err != nil { return nil, err } - exportOpts := unversioned.ExportOptions{} - if err := scope.Codec.DecodeParametersInto(req.Request.URL.Query(), &exportOpts); err != nil { - return nil, err - } - if exportOpts.Export { - return nil, errors.NewBadRequest("export unsupported") - } return r.Get(ctx, name, opts) }) } -func getRequestOptions(req *restful.Request, scope RequestScope, internalKind, externalKind unversioned.GroupVersionKind, subpath bool, subpathKey string) (runtime.Object, error) { - if internalKind.IsEmpty() { - return nil, nil +func getRequestOptions(req *restful.Request, scope RequestScope, into runtime.Object, subpath bool, subpathKey string) error { + if into == nil { + return nil } query := req.Request.URL.Query() @@ -168,37 +169,23 @@ func getRequestOptions(req *restful.Request, scope RequestScope, internalKind, e newQuery[subpathKey] = []string{req.PathParameter("path")} query = newQuery } - - versioned, err := scope.Creater.New(externalKind) - if err != nil { - return nil, err - } - - if err := scope.Codec.DecodeParametersInto(query, versioned); err != nil { - return nil, errors.NewBadRequest(err.Error()) - } - out, err := scope.Convertor.ConvertToVersion(versioned, internalKind.GroupVersion().String()) - if err != nil { - // programmer error - return nil, err - } - return out, nil + return scope.ParameterCodec.DecodeParameters(query, scope.Kind.GroupVersion(), into) } // ConnectResource returns a function that handles a connect request on a rest.Storage object. -func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, internalKind, externalKind unversioned.GroupVersionKind, restPath string, subpath bool, subpathKey string) restful.RouteFunction { +func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admission.Interface, restPath string) restful.RouteFunction { return func(req *restful.Request, res *restful.Response) { w := res.ResponseWriter namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) ctx = api.WithNamespace(ctx, namespace) - opts, err := getRequestOptions(req, scope, internalKind, externalKind, subpath, subpathKey) - if err != nil { - errorJSON(err, scope.Codec, w) + opts, subpath, subpathKey := connecter.NewConnectOptions() + if err := getRequestOptions(req, scope, opts, subpath, subpathKey); err != nil { + scope.err(err, req, res) return } if admit.Handles(admission.Connect) { @@ -211,13 +198,13 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi err = admit.Admit(admission.NewAttributesRecord(connectRequest, scope.Kind.GroupKind(), namespace, name, scope.Resource.GroupResource(), scope.Subresource, admission.Connect, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } - handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req.Request, w: w}) + handler, err := connecter.Connect(ctx, name, opts, &responder{scope: scope, req: req, res: res}) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } handler.ServeHTTP(w, req.Request) @@ -227,16 +214,16 @@ func ConnectResource(connecter rest.Connecter, scope RequestScope, admit admissi // responder implements rest.Responder for assisting a connector in writing objects or errors. type responder struct { scope RequestScope - req *http.Request - w http.ResponseWriter + req *restful.Request + res *restful.Response } func (r *responder) Object(statusCode int, obj runtime.Object) { - write(statusCode, r.scope.Kind.GroupVersion(), r.scope.Codec, obj, r.w, r.req) + write(statusCode, r.scope.Kind.GroupVersion(), r.scope.Serializer, obj, r.res.ResponseWriter, r.req.Request) } func (r *responder) Error(err error) { - errorJSON(err, r.scope.Codec, r.w) + r.scope.err(err, r.req, r.res) } // ListResource returns a function that handles retrieving a list of resources from a rest.Storage object. @@ -249,7 +236,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch namespace, err := scope.Namer.Namespace(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -264,19 +251,9 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch ctx := scope.ContextFunc(req) ctx = api.WithNamespace(ctx, namespace) - listOptionsGVK := scope.Kind.GroupVersion().WithKind("ListOptions") - versioned, err := scope.Creater.New(listOptionsGVK) - if err != nil { - errorJSON(err, scope.Codec, w) - return - } - if err := scope.Codec.DecodeParametersInto(req.Request.URL.Query(), versioned); err != nil { - errorJSON(err, scope.Codec, w) - return - } opts := api.ListOptions{} - if err := scope.Convertor.Convert(versioned, &opts); err != nil { - errorJSON(err, scope.Codec, w) + if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &opts); err != nil { + scope.err(err, req, res) return } @@ -289,7 +266,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch if opts.FieldSelector, err = opts.FieldSelector.Transform(fn); err != nil { // TODO: allow bad request to set field causes based on query parameters err = errors.NewBadRequest(err.Error()) - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -305,11 +282,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch // and a field selector, since just the name is // sufficient to narrow down the request to a // single object. - errorJSON( - errors.NewBadRequest("both a name and a field selector provided; please provide one or the other."), - scope.Codec, - w, - ) + scope.err(errors.NewBadRequest("both a name and a field selector provided; please provide one or the other."), req, res) return } opts.FieldSelector = nameSelector @@ -318,7 +291,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch if (opts.Watch || forceWatch) && rw != nil { watcher, err := rw.Watch(ctx, &opts) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } // TODO: Currently we explicitly ignore ?timeout= and use only ?timeoutSeconds=. @@ -329,7 +302,7 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch if timeout == 0 && minRequestTimeout > 0 { timeout = time.Duration(float64(minRequestTimeout) * (rand.Float64() + 1.0)) } - serveWatch(watcher, scope, w, req, timeout) + serveWatch(watcher, scope, req, res, timeout) return } @@ -338,17 +311,17 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch trace.Step("About to List from storage") result, err := r.List(ctx, &opts) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Listing from storage done") numberOfItems, err := setListSelfLink(result, req, scope.Namer) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Self-linking done") - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) trace.Step(fmt.Sprintf("Writing http response done (%d items)", numberOfItems)) } } @@ -374,25 +347,39 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object namespace, err = scope.Namer.Namespace(req) } if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) ctx = api.WithNamespace(ctx, namespace) + gv := scope.Kind.GroupVersion() + s, err := negotiateInputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + decoder := scope.Serializer.DecoderToVersion(s, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}) + body, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } - obj := r.New() + defaultGVK := scope.Kind + original := r.New() trace.Step("About to convert to expected version") - // TODO this cleans up with proper typing - if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.Kind); err != nil { - err = transformDecodeError(typer, err, obj, body) - errorJSON(err, scope.Codec, w) + obj, gvk, err := decoder.Decode(body, &defaultGVK, original) + if err != nil { + err = transformDecodeError(typer, err, original, gvk) + scope.err(err, req, res) + return + } + if gvk.GroupVersion() != gv { + err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%v)", gvk.GroupVersion().String(), gv.String())) + scope.err(err, req, res) return } trace.Step("Conversion done") @@ -402,7 +389,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object err = admit.Admit(admission.NewAttributesRecord(obj, scope.Kind.GroupKind(), namespace, name, scope.Resource.GroupResource(), scope.Subresource, admission.Create, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -416,18 +403,18 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object return out, err }) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Object stored in database") if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Self-link added") - write(http.StatusCreated, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusCreated, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -462,7 +449,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -471,10 +458,11 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper versionedObj, err := converter.ConvertToVersion(r.New(), scope.Kind.GroupVersion().String()) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } + // TODO: handle this in negotiation contentType := req.HeaderParameter("Content-Type") // Remove "; charset=" if included in header. if idx := strings.Index(contentType, ";"); idx > 0 { @@ -484,10 +472,21 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper patchJS, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } + s, ok := scope.Serializer.SerializerForMediaType("application/json", nil) + if !ok { + scope.err(fmt.Errorf("no serializer defined for JSON"), req, res) + return + } + gv := scope.Kind.GroupVersion() + codec := runtime.NewCodec( + scope.Serializer.EncoderForVersion(s, gv), + scope.Serializer.DecoderToVersion(s, unversioned.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}), + ) + updateAdmit := func(updatedObject runtime.Object) error { if admit != nil && admit.Handles(admission.Update) { userInfo, _ := api.UserFrom(ctx) @@ -497,18 +496,18 @@ func PatchResource(r rest.Patcher, scope RequestScope, typer runtime.ObjectTyper return nil } - result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS, scope.Namer, scope.Codec) + result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS, scope.Namer, codec) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -625,7 +624,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) @@ -633,21 +632,33 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType body, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } - obj := r.New() + s, err := negotiateInputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + defaultGVK := scope.Kind + original := r.New() trace.Step("About to convert to expected version") - if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.Kind); err != nil { - err = transformDecodeError(typer, err, obj, body) - errorJSON(err, scope.Codec, w) + obj, gvk, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, original) + if err != nil { + err = transformDecodeError(typer, err, original, gvk) + scope.err(err, req, res) + return + } + if gvk.GroupVersion() != defaultGVK.GroupVersion() { + err = errors.NewBadRequest(fmt.Sprintf("the API version in the data (%s) does not match the expected API version (%s)", gvk.GroupVersion(), defaultGVK.GroupVersion())) + scope.err(err, req, res) return } trace.Step("Conversion done") if err := checkName(obj, name, namespace, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -656,7 +667,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType err = admit.Admit(admission.NewAttributesRecord(obj, scope.Kind.GroupKind(), namespace, name, scope.Resource.GroupResource(), scope.Subresource, admission.Update, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -669,13 +680,13 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType return obj, err }) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Object stored in database") if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Self-link added") @@ -684,7 +695,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType if wasCreated { status = http.StatusCreated } - writeJSON(status, scope.Codec, result, w, isPrettyPrint(req.Request)) + write(status, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -702,7 +713,7 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, namespace, name, err := scope.Namer.Name(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } ctx := scope.ContextFunc(req) @@ -712,12 +723,23 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, if checkBody { body, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } if len(body) > 0 { - if err := scope.Codec.DecodeInto(body, options); err != nil { - errorJSON(err, scope.Codec, w) + s, err := negotiateInputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions") + obj, _, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options) + if err != nil { + scope.err(err, req, res) + return + } + if obj != options { + scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), req, res) return } } @@ -728,7 +750,7 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, err = admit.Admit(admission.NewAttributesRecord(nil, scope.Kind.GroupKind(), namespace, name, scope.Resource.GroupResource(), scope.Subresource, admission.Delete, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -738,7 +760,7 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, return r.Delete(ctx, name, options) }) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } trace.Step("Object deleted from database") @@ -758,12 +780,12 @@ func DeleteResource(r rest.GracefulDeleter, checkBody bool, scope RequestScope, // when a non-status response is returned, set the self link if _, ok := result.(*unversioned.Status); !ok { if err := setSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } } - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + write(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) } } @@ -777,7 +799,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco namespace, err := scope.Namer.Namespace(req) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -789,24 +811,14 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco err = admit.Admit(admission.NewAttributesRecord(nil, scope.Kind.GroupKind(), namespace, "", scope.Resource.GroupResource(), scope.Subresource, admission.Delete, userInfo)) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } - listOptionsGVK := scope.Kind.GroupVersion().WithKind("ListOptions") - versioned, err := scope.Creater.New(listOptionsGVK) - if err != nil { - errorJSON(err, scope.Codec, w) - return - } - if err := scope.Codec.DecodeParametersInto(req.Request.URL.Query(), versioned); err != nil { - errorJSON(err, scope.Codec, w) - return - } listOptions := api.ListOptions{} - if err := scope.Convertor.Convert(versioned, &listOptions); err != nil { - errorJSON(err, scope.Codec, w) + if err := scope.ParameterCodec.DecodeParameters(req.Request.URL.Query(), scope.Kind.GroupVersion(), &listOptions); err != nil { + scope.err(err, req, res) return } @@ -819,7 +831,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco if listOptions.FieldSelector, err = listOptions.FieldSelector.Transform(fn); err != nil { // TODO: allow bad request to set field causes based on query parameters err = errors.NewBadRequest(err.Error()) - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } @@ -828,12 +840,23 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco if checkBody { body, err := readBody(req.Request) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } if len(body) > 0 { - if err := scope.Codec.DecodeInto(body, options); err != nil { - errorJSON(err, scope.Codec, w) + s, err := negotiateInputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + defaultGVK := scope.Kind.GroupVersion().WithKind("DeleteOptions") + obj, _, err := scope.Serializer.DecoderToVersion(s, defaultGVK.GroupVersion()).Decode(body, &defaultGVK, options) + if err != nil { + scope.err(err, req, res) + return + } + if obj != options { + scope.err(fmt.Errorf("decoded object cannot be converted to DeleteOptions"), req, res) return } } @@ -843,7 +866,7 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco return r.DeleteCollection(ctx, options, &listOptions) }) if err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } @@ -861,12 +884,12 @@ func DeleteCollection(r rest.CollectionDeleter, checkBody bool, scope RequestSco // when a non-status response is returned, set the self link if _, ok := result.(*unversioned.Status); !ok { if _, err := setListSelfLink(result, req, scope.Namer); err != nil { - errorJSON(err, scope.Codec, w) + scope.err(err, req, res) return } } } - write(http.StatusOK, scope.Kind.GroupVersion(), scope.Codec, result, w, req.Request) + writeNegotiated(scope.Serializer, scope.Kind.GroupVersion(), w, req.Request, http.StatusOK, result) } } @@ -911,15 +934,15 @@ func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, } // transformDecodeError adds additional information when a decode fails. -func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, body []byte) error { - objectGroupVersionKind, err := typer.ObjectKind(into) +func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, gvk *unversioned.GroupVersionKind) error { + objGVK, err := typer.ObjectKind(into) if err != nil { return err } - if dataGroupVersionKind, err := typer.DataKind(body); err == nil && len(dataGroupVersionKind.Kind) > 0 { - return errors.NewBadRequest(fmt.Sprintf("%s in version %v cannot be handled as a %s: %v", dataGroupVersionKind.Kind, dataGroupVersionKind.GroupVersion(), objectGroupVersionKind.Kind, baseErr)) + if gvk != nil && len(gvk.Kind) > 0 { + return errors.NewBadRequest(fmt.Sprintf("%s in version %q cannot be handled as a %s: %v", gvk.Kind, gvk.Version, objGVK.Kind, baseErr)) } - return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v", objectGroupVersionKind.Kind, baseErr)) + return errors.NewBadRequest(fmt.Sprintf("the object provided is unrecognized (must be of type %s): %v", objGVK.Kind, baseErr)) } // setSelfLink sets the self link of an object (or the child items in a list) to the base URL of the request diff --git a/pkg/apiserver/resthandler_test.go b/pkg/apiserver/resthandler_test.go index 53d6f0c86c0..50477fc89ad 100644 --- a/pkg/apiserver/resthandler_test.go +++ b/pkg/apiserver/resthandler_test.go @@ -28,8 +28,8 @@ import ( "k8s.io/kubernetes/pkg/api" apierrors "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/strategicpatch" @@ -155,7 +155,7 @@ func (tc *patchTestCase) Run(t *testing.T) { namespace := tc.startingPod.Namespace name := tc.startingPod.Name - codec := registered.GroupOrDie(api.GroupName).Codec + codec := testapi.Default.Codec() admit := tc.admit if admit == nil { admit = func(updatedObject runtime.Object) error { diff --git a/pkg/apiserver/watch.go b/pkg/apiserver/watch.go index 3519e729079..8b951d9e539 100644 --- a/pkg/apiserver/watch.go +++ b/pkg/apiserver/watch.go @@ -65,23 +65,34 @@ func (w *realTimeoutFactory) TimeoutCh() (<-chan time.Time, func() bool) { } // serveWatch handles serving requests to the server -func serveWatch(watcher watch.Interface, scope RequestScope, w http.ResponseWriter, req *restful.Request, timeout time.Duration) { - watchServer := &WatchServer{watcher, scope.Codec, func(obj runtime.Object) { +func serveWatch(watcher watch.Interface, scope RequestScope, req *restful.Request, res *restful.Response, timeout time.Duration) { + s, mediaType, err := negotiateOutputSerializer(req.Request, scope.Serializer) + if err != nil { + scope.err(err, req, res) + return + } + // TODO: replace with typed serialization + if mediaType != "application/json" { + writeRawJSON(http.StatusNotAcceptable, (errNotAcceptable{[]string{"application/json"}}).Status(), res.ResponseWriter) + return + } + encoder := scope.Serializer.EncoderForVersion(s, scope.Kind.GroupVersion()) + watchServer := &WatchServer{watcher, encoder, func(obj runtime.Object) { if err := setSelfLink(obj, req, scope.Namer); err != nil { glog.V(5).Infof("Failed to set self link for object %v: %v", reflect.TypeOf(obj), err) } }, &realTimeoutFactory{timeout}} if isWebsocketRequest(req.Request) { - websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(w), req.Request) + websocket.Handler(watchServer.HandleWS).ServeHTTP(httplog.Unlogged(res.ResponseWriter), req.Request) } else { - watchServer.ServeHTTP(w, req.Request) + watchServer.ServeHTTP(res.ResponseWriter, req.Request) } } // WatchServer serves a watch.Interface over a websocket or vanilla HTTP. type WatchServer struct { watching watch.Interface - codec runtime.Codec + encoder runtime.Encoder fixup func(runtime.Object) t timeoutFactory } @@ -108,7 +119,7 @@ func (w *WatchServer) HandleWS(ws *websocket.Conn) { return } w.fixup(event.Object) - obj, err := watchjson.Object(w.codec, &event) + obj, err := watchjson.Object(w.encoder, &event) if err != nil { // Client disconnect. w.watching.Stop() @@ -134,20 +145,21 @@ func (self *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { cn, ok := w.(http.CloseNotifier) if !ok { - loggedW.Addf("unable to get CloseNotifier") + loggedW.Addf("unable to get CloseNotifier: %#v", w) http.NotFound(w, req) return } flusher, ok := w.(http.Flusher) if !ok { - loggedW.Addf("unable to get Flusher") + loggedW.Addf("unable to get Flusher: %#v", w) http.NotFound(w, req) return } w.Header().Set("Transfer-Encoding", "chunked") w.WriteHeader(http.StatusOK) flusher.Flush() - encoder := watchjson.NewEncoder(w, self.codec) + // TODO: use arbitrary serialization on watch + encoder := watchjson.NewEncoder(w, self.encoder) for { select { case <-cn.CloseNotify(): diff --git a/pkg/apiserver/watch_test.go b/pkg/apiserver/watch_test.go index 130a799c19a..4ff824e000c 100644 --- a/pkg/apiserver/watch_test.go +++ b/pkg/apiserver/watch_test.go @@ -169,6 +169,34 @@ func TestWatchHTTP(t *testing.T) { } } +func TestWatchHTTPAccept(t *testing.T) { + simpleStorage := &SimpleRESTStorage{} + handler := handle(map[string]rest.Storage{"simples": simpleStorage}) + server := httptest.NewServer(handler) + defer server.Close() + client := http.Client{} + + dest, _ := url.Parse(server.URL) + dest.Path = "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/simples" + dest.RawQuery = "" + + request, err := http.NewRequest("GET", dest.String(), nil) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + request.Header.Set("Accept", "application/yaml") + response, err := client.Do(request) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + // TODO: once this is fixed, this test will change + if response.StatusCode != http.StatusNotAcceptable { + t.Errorf("Unexpected response %#v", response) + } +} + func TestWatchParamParsing(t *testing.T) { simpleStorage := &SimpleRESTStorage{} handler := handle(map[string]rest.Storage{ diff --git a/pkg/auth/authorizer/abac/abac.go b/pkg/auth/authorizer/abac/abac.go index 0ef8768714a..c3bfedcc4d8 100644 --- a/pkg/auth/authorizer/abac/abac.go +++ b/pkg/auth/authorizer/abac/abac.go @@ -28,11 +28,11 @@ import ( "github.com/golang/glog" - "k8s.io/kubernetes/pkg/apis/abac" - "k8s.io/kubernetes/pkg/apis/abac/latest" + api "k8s.io/kubernetes/pkg/apis/abac" + _ "k8s.io/kubernetes/pkg/apis/abac/latest" "k8s.io/kubernetes/pkg/apis/abac/v0" - _ "k8s.io/kubernetes/pkg/apis/abac/v1beta1" "k8s.io/kubernetes/pkg/auth/authorizer" + "k8s.io/kubernetes/pkg/runtime" ) type policyLoadError struct { @@ -64,6 +64,8 @@ func NewFromFile(path string) (policyList, error) { scanner := bufio.NewScanner(file) pl := make(policyList, 0) + decoder := api.Codecs.UniversalDecoder() + i := 0 unversionedLines := 0 for scanner.Scan() { @@ -77,34 +79,29 @@ func NewFromFile(path string) (policyList, error) { continue } - dataKind, err := api.Scheme.DataKind(b) + decodedObj, _, err := decoder.Decode(b, nil, nil) if err != nil { - return nil, policyLoadError{path, i, b, err} - } - - if dataKind.IsEmpty() { + if !(runtime.IsMissingVersion(err) || runtime.IsMissingKind(err) || runtime.IsNotRegisteredError(err)) { + return nil, policyLoadError{path, i, b, err} + } unversionedLines++ // Migrate unversioned policy object oldPolicy := &v0.Policy{} - if err := latest.Codec.DecodeInto(b, oldPolicy); err != nil { + if err := runtime.DecodeInto(decoder, b, oldPolicy); err != nil { return nil, policyLoadError{path, i, b, err} } if err := api.Scheme.Convert(oldPolicy, p); err != nil { return nil, policyLoadError{path, i, b, err} } - } else { - decodedObj, err := latest.Codec.Decode(b) - if err != nil { - return nil, policyLoadError{path, i, b, err} - } - decodedPolicy, ok := decodedObj.(*api.Policy) - if !ok { - return nil, policyLoadError{path, i, b, fmt.Errorf("unrecognized object: %#v", decodedObj)} - } - p = decodedPolicy + pl = append(pl, p) + continue } - pl = append(pl, p) + decodedPolicy, ok := decodedObj.(*api.Policy) + if !ok { + return nil, policyLoadError{path, i, b, fmt.Errorf("unrecognized object: %#v", decodedObj)} + } + pl = append(pl, decodedPolicy) } if unversionedLines > 0 { diff --git a/pkg/auth/authorizer/abac/abac_test.go b/pkg/auth/authorizer/abac/abac_test.go index 5fa1154fd3f..8b4e3b75ba2 100644 --- a/pkg/auth/authorizer/abac/abac_test.go +++ b/pkg/auth/authorizer/abac/abac_test.go @@ -21,7 +21,7 @@ import ( "os" "testing" - "k8s.io/kubernetes/pkg/apis/abac" + api "k8s.io/kubernetes/pkg/apis/abac" "k8s.io/kubernetes/pkg/apis/abac/v0" "k8s.io/kubernetes/pkg/apis/abac/v1beta1" "k8s.io/kubernetes/pkg/auth/authorizer" diff --git a/pkg/client/testing/core/fixture.go b/pkg/client/testing/core/fixture.go index 19824aa453a..6bac2b337b8 100644 --- a/pkg/client/testing/core/fixture.go +++ b/pkg/client/testing/core/fixture.go @@ -128,7 +128,7 @@ type objects struct { types map[string][]runtime.Object last map[string]int scheme ObjectScheme - decoder runtime.ObjectDecoder + decoder runtime.Decoder } var _ ObjectRetriever = &objects{} @@ -143,7 +143,7 @@ var _ ObjectRetriever = &objects{} // as a runtime.Object if Status == Success). If multiple PodLists are provided, they // will be returned in order by the Kind call, and the last PodList will be reused for // subsequent calls. -func NewObjects(scheme ObjectScheme, decoder runtime.ObjectDecoder) ObjectRetriever { +func NewObjects(scheme ObjectScheme, decoder runtime.Decoder) ObjectRetriever { return objects{ types: make(map[string][]runtime.Object), last: make(map[string]int), diff --git a/pkg/client/testing/fake/clientset.go b/pkg/client/testing/fake/clientset.go index 3ddacfea171..e14b0dc08bd 100644 --- a/pkg/client/testing/fake/clientset.go +++ b/pkg/client/testing/fake/clientset.go @@ -30,7 +30,7 @@ import ( // Clientset returns a clientset that will respond with the provided objects func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := core.NewObjects(api.Scheme, api.Scheme) + o := core.NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) for _, obj := range objects { if err := o.Add(obj); err != nil { panic(err) diff --git a/pkg/client/typed/generated/extensions/unversioned/extensions_client.go b/pkg/client/typed/generated/extensions/unversioned/extensions_client.go index bfbf8aefd30..9836bf0c6fa 100644 --- a/pkg/client/typed/generated/extensions/unversioned/extensions_client.go +++ b/pkg/client/typed/generated/extensions/unversioned/extensions_client.go @@ -17,7 +17,7 @@ limitations under the License. package unversioned import ( - "fmt" + api "k8s.io/kubernetes/pkg/api" registered "k8s.io/kubernetes/pkg/apimachinery/registered" unversioned "k8s.io/kubernetes/pkg/client/unversioned" ) @@ -109,12 +109,7 @@ func setConfigDefaults(config *unversioned.Config) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("Extensions API version '%s' is not recognized (valid values: %s)", - config.GroupVersion, g.GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/pkg/client/typed/generated/legacy/unversioned/legacy_client.go b/pkg/client/typed/generated/legacy/unversioned/legacy_client.go index afeedab1789..0f5b12a6f92 100644 --- a/pkg/client/typed/generated/legacy/unversioned/legacy_client.go +++ b/pkg/client/typed/generated/legacy/unversioned/legacy_client.go @@ -17,7 +17,7 @@ limitations under the License. package unversioned import ( - "fmt" + api "k8s.io/kubernetes/pkg/api" registered "k8s.io/kubernetes/pkg/apimachinery/registered" unversioned "k8s.io/kubernetes/pkg/client/unversioned" ) @@ -149,12 +149,7 @@ func setConfigDefaults(config *unversioned.Config) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("Legacy API version '%s' is not recognized (valid values: %s)", - config.GroupVersion, g.GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/pkg/client/unversioned/client_test.go b/pkg/client/unversioned/client_test.go index 64bf256aade..8fdfe4ecd24 100644 --- a/pkg/client/unversioned/client_test.go +++ b/pkg/client/unversioned/client_test.go @@ -75,7 +75,7 @@ func TestGetServerGroupsWithV1Server(t *testing.T) { } output, err := json.Marshal(obj) if err != nil { - t.Errorf("unexpected encoding error: %v", err) + t.Fatalf("unexpected encoding error: %v", err) return } w.Header().Set("Content-Type", "application/json") @@ -88,7 +88,7 @@ func TestGetServerGroupsWithV1Server(t *testing.T) { // ServerGroups should not return an error even if server returns error at /api and /apis apiGroupList, err := client.Discovery().ServerGroups() if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } groupVersions := ExtractGroupVersions(apiGroupList) if !reflect.DeepEqual(groupVersions, []string{"v1"}) { diff --git a/pkg/client/unversioned/clientcmd/api/latest/latest.go b/pkg/client/unversioned/clientcmd/api/latest/latest.go index ecce3cbdd51..90d5c5380b1 100644 --- a/pkg/client/unversioned/clientcmd/api/latest/latest.go +++ b/pkg/client/unversioned/clientcmd/api/latest/latest.go @@ -18,8 +18,11 @@ package latest import ( "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1" + "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" + _ "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/v1" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" + "k8s.io/kubernetes/pkg/runtime/serializer/versioning" ) // Version is the string that represents the current external default version. @@ -37,8 +40,9 @@ const OldestVersion = "v1" // with a set of versions to choose. var Versions = []string{"v1"} -// Codec is the default codec for serializing output that should use -// the latest supported version. Use this Codec when writing to -// disk, a data store that is not dynamically versioned, or in tests. -// This codec can decode any object that Kubernetes is aware of. -var Codec = runtime.YAMLDecoder(v1.Codec) +var Codec = versioning.NewCodecForScheme( + api.Scheme, + json.NewYAMLSerializer(json.DefaultMetaFactory, api.Scheme, runtime.ObjectTyperToTyper(api.Scheme)), + []unversioned.GroupVersion{{Version: Version}}, + []unversioned.GroupVersion{{Version: runtime.APIVersionInternal}}, +) diff --git a/pkg/client/unversioned/clientcmd/api/register.go b/pkg/client/unversioned/clientcmd/api/register.go index f3be591718b..e4e23998abb 100644 --- a/pkg/client/unversioned/clientcmd/api/register.go +++ b/pkg/client/unversioned/clientcmd/api/register.go @@ -26,7 +26,7 @@ var Scheme = runtime.NewScheme() // SchemeGroupVersion is group version used to register these objects // TODO this should be in the "kubeconfig" group -var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: runtime.APIVersionInternal} func init() { Scheme.AddKnownTypes(SchemeGroupVersion, diff --git a/pkg/client/unversioned/clientcmd/api/types.go b/pkg/client/unversioned/clientcmd/api/types.go index 818ab25b2a8..4a1dc364f43 100644 --- a/pkg/client/unversioned/clientcmd/api/types.go +++ b/pkg/client/unversioned/clientcmd/api/types.go @@ -42,14 +42,14 @@ type Config struct { // CurrentContext is the name of the context that you would like to use by default CurrentContext string `json:"current-context"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // IMPORTANT if you add fields to this struct, please update IsConfigEmpty() type Preferences struct { Colors bool `json:"colors,omitempty"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // Cluster contains information about how to communicate with a kubernetes cluster @@ -67,7 +67,7 @@ type Cluster struct { // CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // AuthInfo contains information that describes identity information. This is use to tell the kubernetes cluster who you are. @@ -89,7 +89,7 @@ type AuthInfo struct { // Password is the password for basic authentication to the kubernetes cluster. Password string `json:"password,omitempty"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), a user (how do I identify myself), and a namespace (what subset of resources do I want to work with) @@ -103,7 +103,7 @@ type Context struct { // Namespace is the default namespace to use on unspecified requests Namespace string `json:"namespace,omitempty"` // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions map[string]*runtime.EmbeddedObject `json:"extensions,omitempty"` + Extensions map[string]runtime.Object `json:"extensions,omitempty"` } // NewConfig is a convenience function that returns a new Config object with non-nil maps @@ -113,26 +113,26 @@ func NewConfig() *Config { Clusters: make(map[string]*Cluster), AuthInfos: make(map[string]*AuthInfo), Contexts: make(map[string]*Context), - Extensions: make(map[string]*runtime.EmbeddedObject), + Extensions: make(map[string]runtime.Object), } } // NewConfig is a convenience function that returns a new Config object with non-nil maps func NewContext() *Context { - return &Context{Extensions: make(map[string]*runtime.EmbeddedObject)} + return &Context{Extensions: make(map[string]runtime.Object)} } // NewConfig is a convenience function that returns a new Config object with non-nil maps func NewCluster() *Cluster { - return &Cluster{Extensions: make(map[string]*runtime.EmbeddedObject)} + return &Cluster{Extensions: make(map[string]runtime.Object)} } // NewConfig is a convenience function that returns a new Config object with non-nil maps func NewAuthInfo() *AuthInfo { - return &AuthInfo{Extensions: make(map[string]*runtime.EmbeddedObject)} + return &AuthInfo{Extensions: make(map[string]runtime.Object)} } // NewConfig is a convenience function that returns a new Config object with non-nil maps func NewPreferences() *Preferences { - return &Preferences{Extensions: make(map[string]*runtime.EmbeddedObject)} + return &Preferences{Extensions: make(map[string]runtime.Object)} } diff --git a/pkg/client/unversioned/clientcmd/api/v1/conversion.go b/pkg/client/unversioned/clientcmd/api/v1/conversion.go index cffdeb6ea9e..e03fc60b164 100644 --- a/pkg/client/unversioned/clientcmd/api/v1/conversion.go +++ b/pkg/client/unversioned/clientcmd/api/v1/conversion.go @@ -69,7 +69,7 @@ func init() { if err := s.Convert(&in.Contexts, &out.Contexts, 0); err != nil { return err } - out.Extensions = make(map[string]*runtime.EmbeddedObject) + out.Extensions = make(map[string]runtime.Object) if err := s.Convert(&in.Extensions, &out.Extensions, 0); err != nil { return err } @@ -192,10 +192,10 @@ func init() { return nil }, - func(in *[]NamedExtension, out *map[string]*runtime.EmbeddedObject, s conversion.Scope) error { + func(in *[]NamedExtension, out *map[string]runtime.Object, s conversion.Scope) error { for _, curr := range *in { - newExtension := &runtime.EmbeddedObject{} - if err := s.Convert(&curr.Extension, newExtension, 0); err != nil { + var newExtension runtime.Object + if err := s.Convert(&curr.Extension, &newExtension, 0); err != nil { return err } (*out)[curr.Name] = newExtension @@ -203,7 +203,7 @@ func init() { return nil }, - func(in *map[string]*runtime.EmbeddedObject, out *[]NamedExtension, s conversion.Scope) error { + func(in *map[string]runtime.Object, out *[]NamedExtension, s conversion.Scope) error { allKeys := make([]string, 0, len(*in)) for key := range *in { allKeys = append(allKeys, key) diff --git a/pkg/client/unversioned/clientcmd/api/v1/register.go b/pkg/client/unversioned/clientcmd/api/v1/register.go index 055ce52d3ae..edf9fe1a70a 100644 --- a/pkg/client/unversioned/clientcmd/api/v1/register.go +++ b/pkg/client/unversioned/clientcmd/api/v1/register.go @@ -19,16 +19,12 @@ package v1 import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - "k8s.io/kubernetes/pkg/runtime" ) // SchemeGroupVersion is group version used to register these objects // TODO this should be in the "kubeconfig" group var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"} -// Codec encodes internal objects to the v1 scheme -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func init() { api.Scheme.AddKnownTypes(SchemeGroupVersion, &Config{}, diff --git a/pkg/client/unversioned/clientcmd/loader.go b/pkg/client/unversioned/clientcmd/loader.go index 42f1210d5b0..55b3dbcd2ce 100644 --- a/pkg/client/unversioned/clientcmd/loader.go +++ b/pkg/client/unversioned/clientcmd/loader.go @@ -25,12 +25,13 @@ import ( "path/filepath" "strings" - "github.com/ghodss/yaml" "github.com/golang/glog" "github.com/imdario/mergo" + "k8s.io/kubernetes/pkg/api/unversioned" clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" clientcmdlatest "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest" + "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" ) @@ -263,11 +264,11 @@ func Load(data []byte) (*clientcmdapi.Config, error) { if len(data) == 0 { return config, nil } - - if err := clientcmdlatest.Codec.DecodeInto(data, config); err != nil { + decoded, _, err := clientcmdlatest.Codec.Decode(data, &unversioned.GroupVersionKind{Version: clientcmdlatest.Version, Kind: "Config"}, config) + if err != nil { return nil, err } - return config, nil + return decoded.(*clientcmdapi.Config), nil } // WriteToFile serializes the config to yaml and writes it out to a file. If not present, it creates the file with the mode 0600. If it is present @@ -292,15 +293,7 @@ func WriteToFile(config clientcmdapi.Config, filename string) error { // Write serializes the config to yaml. // Encapsulates serialization without assuming the destination is a file. func Write(config clientcmdapi.Config) ([]byte, error) { - json, err := clientcmdlatest.Codec.Encode(&config) - if err != nil { - return nil, err - } - content, err := yaml.JSONToYAML(json) - if err != nil { - return nil, err - } - return content, nil + return runtime.Encode(clientcmdlatest.Codec, &config) } func (rules ClientConfigLoadingRules) ResolvePaths() bool { diff --git a/pkg/client/unversioned/clientcmd/loader_test.go b/pkg/client/unversioned/clientcmd/loader_test.go index 22926e7752a..0921f7043ae 100644 --- a/pkg/client/unversioned/clientcmd/loader_test.go +++ b/pkg/client/unversioned/clientcmd/loader_test.go @@ -30,6 +30,7 @@ import ( clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" clientcmdlatest "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api/latest" + "k8s.io/kubernetes/pkg/runtime" ) var ( @@ -391,7 +392,7 @@ func ExampleNoMergingOnExplicitPaths() { mergedConfig, err := loadingRules.Load() - json, err := clientcmdlatest.Codec.Encode(mergedConfig) + json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) if err != nil { fmt.Printf("Unexpected error: %v", err) } @@ -437,7 +438,7 @@ func ExampleMergingSomeWithConflict() { mergedConfig, err := loadingRules.Load() - json, err := clientcmdlatest.Codec.Encode(mergedConfig) + json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) if err != nil { fmt.Printf("Unexpected error: %v", err) } @@ -496,7 +497,7 @@ func ExampleMergingEverythingNoConflicts() { mergedConfig, err := loadingRules.Load() - json, err := clientcmdlatest.Codec.Encode(mergedConfig) + json, err := runtime.Encode(clientcmdlatest.Codec, mergedConfig) if err != nil { fmt.Printf("Unexpected error: %v", err) } diff --git a/pkg/client/unversioned/discovery_client.go b/pkg/client/unversioned/discovery_client.go index dc0167e20e2..ccc1c1ce8bd 100644 --- a/pkg/client/unversioned/discovery_client.go +++ b/pkg/client/unversioned/discovery_client.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/version" ) @@ -209,8 +210,7 @@ func (d *DiscoveryClient) SwaggerSchema(version unversioned.GroupVersion) (*swag func setDiscoveryDefaults(config *Config) error { config.APIPath = "" config.GroupVersion = nil - // Discovery client deals with unversioned objects, so we use api.Codec. - config.Codec = api.Codec + config.Codec = runtime.NoopEncoder{api.Codecs.UniversalDecoder()} return nil } diff --git a/pkg/client/unversioned/extensions.go b/pkg/client/unversioned/extensions.go index e6be5415ee5..fe041bc987e 100644 --- a/pkg/client/unversioned/extensions.go +++ b/pkg/client/unversioned/extensions.go @@ -17,8 +17,7 @@ limitations under the License. package unversioned import ( - "fmt" - + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/extensions" ) @@ -121,12 +120,7 @@ func setExtensionsDefaults(config *Config) error { config.GroupVersion = ©GroupVersion //} - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("Extensions API group/version '%v' is not recognized (valid values: %v)", - config.GroupVersion, registered.GroupOrDie(extensions.GroupName).GroupVersions) - } - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) if config.QPS == 0 { config.QPS = 5 } diff --git a/pkg/client/unversioned/helper.go b/pkg/client/unversioned/helper.go index ea315c2008d..fad07b60a76 100644 --- a/pkg/client/unversioned/helper.go +++ b/pkg/client/unversioned/helper.go @@ -378,12 +378,8 @@ func SetKubernetesDefaults(config *Config) error { // TODO: Unconditionally set the config.Version, until we fix the config. copyGroupVersion := g.GroupVersion config.GroupVersion = ©GroupVersion - versionInterfaces, err := g.InterfacesFor(*config.GroupVersion) - if err != nil { - return fmt.Errorf("API version '%v' is not recognized (valid values: %v)", *config.GroupVersion, registered.GroupOrDie(api.GroupName).GroupVersions) - } if config.Codec == nil { - config.Codec = versionInterfaces.Codec + config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion) } if config.QPS == 0.0 { config.QPS = 5.0 diff --git a/pkg/client/unversioned/helper_blackbox_test.go b/pkg/client/unversioned/helper_blackbox_test.go index a913886488f..0c961e251e1 100644 --- a/pkg/client/unversioned/helper_blackbox_test.go +++ b/pkg/client/unversioned/helper_blackbox_test.go @@ -56,7 +56,7 @@ func TestNegotiateVersion(t *testing.T) { name: "server supports client default", version: &uapi.GroupVersion{Version: "version1"}, config: &unversioned.Config{}, - serverVersions: []string{"/version1", testapi.Default.GroupVersion().String()}, + serverVersions: []string{"version1", testapi.Default.GroupVersion().String()}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, }, @@ -64,7 +64,7 @@ func TestNegotiateVersion(t *testing.T) { name: "server falls back to client supported", version: testapi.Default.GroupVersion(), config: &unversioned.Config{}, - serverVersions: []string{"/version1"}, + serverVersions: []string{"version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, expectedVersion: &uapi.GroupVersion{Version: "version1"}, }, @@ -78,14 +78,14 @@ func TestNegotiateVersion(t *testing.T) { { name: "explicit version not supported", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, - serverVersions: []string{"/version1"}, + serverVersions: []string{"version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, expectErr: func(err error) bool { return strings.Contains(err.Error(), `server does not support API version "v1"`) }, }, { name: "connection refused error", config: &unversioned.Config{GroupVersion: testapi.Default.GroupVersion()}, - serverVersions: []string{"/version1"}, + serverVersions: []string{"version1"}, clientVersions: []uapi.GroupVersion{{Version: "version1"}, *testapi.Default.GroupVersion()}, sendErr: errors.New("connection refused"), expectErr: func(err error) bool { return strings.Contains(err.Error(), "connection refused") }, diff --git a/pkg/client/unversioned/helper_test.go b/pkg/client/unversioned/helper_test.go index 8921bd04358..8f9b6b3e7b2 100644 --- a/pkg/client/unversioned/helper_test.go +++ b/pkg/client/unversioned/helper_test.go @@ -222,7 +222,7 @@ func TestSetsCodec(t *testing.T) { if e, a := expected.Prefix, client.RESTClient.versionedAPIPath; e != a { t.Errorf("expected %#v, got %#v", e, a) } - if e, a := expected.Codec, client.RESTClient.Codec; e != a { + if e, a := expected.Codec, client.RESTClient.Codec; !reflect.DeepEqual(e, a) { t.Errorf("expected %#v, got %#v", e, a) } } diff --git a/pkg/client/unversioned/request.go b/pkg/client/unversioned/request.go index 371afe3817a..2bcb38fa581 100644 --- a/pkg/client/unversioned/request.go +++ b/pkg/client/unversioned/request.go @@ -452,7 +452,7 @@ func (r *Request) VersionedParams(obj runtime.Object, convertor runtime.ObjectCo continue } if k == unversioned.FieldSelectorQueryParam(r.groupVersion.String()) { - if value == "" { + if len(value) == 0 { // Don't set an empty selector for backward compatibility. // Since there is no way to get the difference between empty // and unspecified string, we don't set it to avoid having @@ -717,7 +717,7 @@ func (r *Request) Stream() (io.ReadCloser, error) { return nil, fmt.Errorf("%v while accessing %v", resp.Status, url) } - if runtimeObject, err := r.codec.Decode(bodyBytes); err == nil { + if runtimeObject, err := runtime.Decode(r.codec, bodyBytes); err == nil { statusError := errors.FromObject(runtimeObject) if _, ok := statusError.(errors.APIStatus); ok { @@ -846,8 +846,10 @@ func (r *Request) transformResponse(resp *http.Response, req *http.Request) Resu // Did the server give us a status response? isStatusResponse := false - var status unversioned.Status - if err := r.codec.DecodeInto(body, &status); err == nil && status.Status != "" { + var status *unversioned.Status + result, err := runtime.Decode(r.codec, body) + if out, ok := result.(*unversioned.Status); err == nil && ok && len(out.Status) > 0 { + status = out isStatusResponse = true } @@ -858,14 +860,14 @@ func (r *Request) transformResponse(resp *http.Response, req *http.Request) Resu if !isStatusResponse { return Result{err: r.transformUnstructuredResponseError(resp, req, body)} } - return Result{err: errors.FromObject(&status)} + return Result{err: errors.FromObject(status)} } // If the server gave us a status back, look at what it was. success := resp.StatusCode >= http.StatusOK && resp.StatusCode <= http.StatusPartialContent if isStatusResponse && (status.Status != unversioned.StatusSuccess && !success) { // "Failed" requests are clearly just an error and it makes sense to return them as such. - return Result{err: errors.FromObject(&status)} + return Result{err: errors.FromObject(status)} } return Result{ @@ -965,7 +967,8 @@ func (r Result) Get() (runtime.Object, error) { if r.err != nil { return nil, r.err } - return r.codec.Decode(r.body) + obj, err := runtime.Decode(r.codec, r.body) + return obj, err } // StatusCode returns the HTTP status code of the request. (Only valid if no @@ -975,12 +978,12 @@ func (r Result) StatusCode(statusCode *int) Result { return r } -// Into stores the result into obj, if possible. +// Into stores the result into obj, if possible. If obj is nil it is ignored. func (r Result) Into(obj runtime.Object) error { if r.err != nil { return r.err } - return r.codec.DecodeInto(r.body, obj) + return runtime.DecodeInto(r.codec, r.body, obj) } // WasCreated updates the provided bool pointer to whether the server returned diff --git a/pkg/client/unversioned/request_test.go b/pkg/client/unversioned/request_test.go index 77d71e8170a..52203af9ad5 100644 --- a/pkg/client/unversioned/request_test.go +++ b/pkg/client/unversioned/request_test.go @@ -698,7 +698,7 @@ func TestDoRequestNewWay(t *testing.T) { Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -861,13 +861,13 @@ func BenchmarkCheckRetryClosesBody(b *testing.B) { func TestDoRequestNewWayReader(t *testing.T) { reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, _ := testapi.Default.Codec().Encode(reqObj) + reqBodyExpected, _ := runtime.Encode(testapi.Default.Codec(), reqObj) expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -902,13 +902,13 @@ func TestDoRequestNewWayReader(t *testing.T) { func TestDoRequestNewWayObj(t *testing.T) { reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, _ := testapi.Default.Codec().Encode(reqObj) + reqBodyExpected, _ := runtime.Encode(testapi.Default.Codec(), reqObj) expectedObj := &api.Service{Spec: api.ServiceSpec{Ports: []api.ServicePort{{ Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -943,7 +943,7 @@ func TestDoRequestNewWayObj(t *testing.T) { func TestDoRequestNewWayFile(t *testing.T) { reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, err := testapi.Default.Codec().Encode(reqObj) + reqBodyExpected, err := runtime.Encode(testapi.Default.Codec(), reqObj) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -964,7 +964,7 @@ func TestDoRequestNewWayFile(t *testing.T) { Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -1000,7 +1000,7 @@ func TestDoRequestNewWayFile(t *testing.T) { func TestWasCreated(t *testing.T) { reqObj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - reqBodyExpected, err := testapi.Default.Codec().Encode(reqObj) + reqBodyExpected, err := runtime.Encode(testapi.Default.Codec(), reqObj) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -1010,7 +1010,7 @@ func TestWasCreated(t *testing.T) { Port: 12345, TargetPort: intstr.FromInt(12345), }}}} - expectedBody, _ := testapi.Default.Codec().Encode(expectedObj) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), expectedObj) fakeHandler := utiltesting.FakeHandler{ StatusCode: 201, ResponseBody: string(expectedBody), @@ -1138,7 +1138,7 @@ func TestBody(t *testing.T) { const data = "test payload" obj := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} - bodyExpected, _ := testapi.Default.Codec().Encode(obj) + bodyExpected, _ := runtime.Encode(testapi.Default.Codec(), obj) f, err := ioutil.TempFile("", "test_body") if err != nil { diff --git a/pkg/client/unversioned/restclient_test.go b/pkg/client/unversioned/restclient_test.go index 69884d86a8a..b59da0fe7ac 100644 --- a/pkg/client/unversioned/restclient_test.go +++ b/pkg/client/unversioned/restclient_test.go @@ -28,13 +28,14 @@ import ( "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" utiltesting "k8s.io/kubernetes/pkg/util/testing" ) func TestDoRequestSuccess(t *testing.T) { status := &unversioned.Status{Status: unversioned.StatusSuccess} - expectedBody, _ := testapi.Default.Codec().Encode(status) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) fakeHandler := utiltesting.FakeHandler{ StatusCode: 200, ResponseBody: string(expectedBody), @@ -60,7 +61,7 @@ func TestDoRequestSuccess(t *testing.T) { if fakeHandler.RequestReceived.Header["Authorization"] == nil { t.Errorf("Request is missing authorization header: %#v", fakeHandler.RequestReceived) } - statusOut, err := testapi.Default.Codec().Decode(body) + statusOut, err := runtime.Decode(testapi.Default.Codec(), body) if err != nil { t.Errorf("Unexpected error %#v", err) } @@ -78,7 +79,7 @@ func TestDoRequestFailed(t *testing.T) { Message: " \"\" not found", Details: &unversioned.StatusDetails{}, } - expectedBody, _ := testapi.Default.Codec().Encode(status) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) fakeHandler := utiltesting.FakeHandler{ StatusCode: 404, ResponseBody: string(expectedBody), @@ -111,7 +112,7 @@ func TestDoRequestFailed(t *testing.T) { func TestDoRequestCreated(t *testing.T) { status := &unversioned.Status{Status: unversioned.StatusSuccess} - expectedBody, _ := testapi.Default.Codec().Encode(status) + expectedBody, _ := runtime.Encode(testapi.Default.Codec(), status) fakeHandler := utiltesting.FakeHandler{ StatusCode: 201, ResponseBody: string(expectedBody), @@ -138,7 +139,7 @@ func TestDoRequestCreated(t *testing.T) { if !created { t.Errorf("Expected object to be created") } - statusOut, err := testapi.Default.Codec().Decode(body) + statusOut, err := runtime.Decode(testapi.Default.Codec(), body) if err != nil { t.Errorf("Unexpected error %#v", err) } diff --git a/pkg/client/unversioned/testclient/fixture.go b/pkg/client/unversioned/testclient/fixture.go index fad088e99de..64388fb6381 100644 --- a/pkg/client/unversioned/testclient/fixture.go +++ b/pkg/client/unversioned/testclient/fixture.go @@ -128,7 +128,7 @@ type objects struct { types map[string][]runtime.Object last map[string]int scheme ObjectScheme - decoder runtime.ObjectDecoder + decoder runtime.Decoder } var _ ObjectRetriever = &objects{} @@ -143,7 +143,7 @@ var _ ObjectRetriever = &objects{} // as a runtime.Object if Status == Success). If multiple PodLists are provided, they // will be returned in order by the Kind call, and the last PodList will be reused for // subsequent calls. -func NewObjects(scheme ObjectScheme, decoder runtime.ObjectDecoder) ObjectRetriever { +func NewObjects(scheme ObjectScheme, decoder runtime.Decoder) ObjectRetriever { return objects{ types: make(map[string][]runtime.Object), last: make(map[string]int), @@ -153,10 +153,7 @@ func NewObjects(scheme ObjectScheme, decoder runtime.ObjectDecoder) ObjectRetrie } func (o objects) Kind(kind unversioned.GroupVersionKind, name string) (runtime.Object, error) { - // TODO our test clients deal in internal versions. We need to plumb that knowledge down here - // we might do this via an extra function to the scheme to allow getting internal group versions - // I'm punting for now - kind.Version = "" + kind.Version = runtime.APIVersionInternal empty, _ := o.scheme.New(kind) nilValue := reflect.Zero(reflect.TypeOf(empty)).Interface().(runtime.Object) diff --git a/pkg/client/unversioned/testclient/simple/simple_testclient.go b/pkg/client/unversioned/testclient/simple/simple_testclient.go index ec1bc0bc78c..60a74963d1a 100644 --- a/pkg/client/unversioned/testclient/simple/simple_testclient.go +++ b/pkg/client/unversioned/testclient/simple/simple_testclient.go @@ -213,7 +213,7 @@ func body(t *testing.T, obj runtime.Object, raw *string) *string { if !found { t.Errorf("Group %s is not registered in testapi", fqKind.GroupVersion().Group) } - bs, err = g.Codec().Encode(obj) + bs, err = runtime.Encode(g.Codec(), obj) if err != nil { t.Errorf("unexpected encoding error: %v", err) } diff --git a/pkg/client/unversioned/testclient/testclient.go b/pkg/client/unversioned/testclient/testclient.go index 8d8d1e92d71..34c2dfb1cb2 100644 --- a/pkg/client/unversioned/testclient/testclient.go +++ b/pkg/client/unversioned/testclient/testclient.go @@ -33,7 +33,7 @@ import ( // NewSimpleFake returns a client that will respond with the provided objects func NewSimpleFake(objects ...runtime.Object) *Fake { - o := NewObjects(api.Scheme, api.Scheme) + o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) for _, obj := range objects { if err := o.Add(obj); err != nil { panic(err) diff --git a/pkg/client/unversioned/testclient/testclient_test.go b/pkg/client/unversioned/testclient/testclient_test.go index 6c7075e2846..b31bdc213ee 100644 --- a/pkg/client/unversioned/testclient/testclient_test.go +++ b/pkg/client/unversioned/testclient/testclient_test.go @@ -26,8 +26,8 @@ import ( ) func TestNewClient(t *testing.T) { - o := NewObjects(api.Scheme, api.Scheme) - if err := AddObjectsFromPath("../../../../examples/guestbook/frontend-service.yaml", o, api.Scheme); err != nil { + o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) + if err := AddObjectsFromPath("../../../../examples/guestbook/frontend-service.yaml", o, api.Codecs.UniversalDecoder()); err != nil { t.Fatal(err) } client := &Fake{} @@ -52,7 +52,7 @@ func TestNewClient(t *testing.T) { } func TestErrors(t *testing.T) { - o := NewObjects(api.Scheme, api.Scheme) + o := NewObjects(api.Scheme, api.Codecs.UniversalDecoder()) o.Add(&api.List{ Items: []runtime.Object{ // This first call to List will return this error diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index b7481d76316..ef1bca9df80 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -24,8 +24,8 @@ import ( "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/client/record" client "k8s.io/kubernetes/pkg/client/unversioned" @@ -259,7 +259,13 @@ func getPodsAnnotationSet(template *api.PodTemplateSpec, object runtime.Object) if err != nil { return desiredAnnotations, fmt.Errorf("unable to get controller reference: %v", err) } - createdByRefJson, err := registered.GroupOrDie(api.GroupName).Codec.Encode(&api.SerializedReference{ + + // TODO: this code was not safe previously - as soon as new code came along that switched to v2, old clients + // would be broken upon reading it. This is explicitly hardcoded to v1 to guarantee predictable deployment. + // We need to consistently handle this case of annotation versioning. + codec := api.Codecs.LegacyCodec(unversioned.GroupVersion{Group: api.GroupName, Version: "v1"}) + + createdByRefJson, err := runtime.Encode(codec, &api.SerializedReference{ Reference: *createdByRef, }) if err != nil { diff --git a/pkg/controller/controller_utils_test.go b/pkg/controller/controller_utils_test.go index 12689300a06..17f054249ee 100644 --- a/pkg/controller/controller_utils_test.go +++ b/pkg/controller/controller_utils_test.go @@ -212,7 +212,7 @@ func TestCreatePods(t *testing.T) { Spec: controllerSpec.Spec.Template.Spec, } fakeHandler.ValidateRequest(t, testapi.Default.ResourcePath("pods", api.NamespaceDefault, ""), "POST", nil) - actualPod, err := client.Codec.Decode([]byte(fakeHandler.RequestBody)) + actualPod, err := runtime.Decode(client.Codec, []byte(fakeHandler.RequestBody)) if err != nil { t.Errorf("Unexpected error: %#v", err) } diff --git a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go index 13c3d30b623..32a0bc70936 100644 --- a/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go +++ b/pkg/controller/persistentvolume/persistentvolume_claim_binder_controller_test.go @@ -275,8 +275,9 @@ func TestExampleObjects(t *testing.T) { } for name, scenario := range scenarios { - o := testclient.NewObjects(api.Scheme, api.Scheme) - if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/"+name, o, api.Scheme); err != nil { + codec := api.Codecs.UniversalDecoder() + o := testclient.NewObjects(api.Scheme, codec) + if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/"+name, o, codec); err != nil { t.Fatal(err) } @@ -332,11 +333,12 @@ func TestExampleObjects(t *testing.T) { } func TestBindingWithExamples(t *testing.T) { - o := testclient.NewObjects(api.Scheme, api.Scheme) - if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/claims/claim-01.yaml", o, api.Scheme); err != nil { + codec := api.Codecs.UniversalDecoder() + o := testclient.NewObjects(api.Scheme, codec) + if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/claims/claim-01.yaml", o, codec); err != nil { t.Fatal(err) } - if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/volumes/local-01.yaml", o, api.Scheme); err != nil { + if err := testclient.AddObjectsFromPath("../../../docs/user-guide/persistent-volumes/volumes/local-01.yaml", o, codec); err != nil { t.Fatal(err) } diff --git a/pkg/conversion/converter.go b/pkg/conversion/converter.go index 8c106d52611..00c9bcb492c 100644 --- a/pkg/conversion/converter.go +++ b/pkg/conversion/converter.go @@ -40,8 +40,11 @@ type DebugLogger interface { type Converter struct { // Map from the conversion pair to a function which can // do the conversion. - conversionFuncs map[typePair]reflect.Value - generatedConversionFuncs map[typePair]reflect.Value + conversionFuncs ConversionFuncs + generatedConversionFuncs ConversionFuncs + + // Set of conversions that should be treated as a no-op + ignoredConversions map[typePair]struct{} // This is a map from a source field type and name, to a list of destination // field type and name. @@ -76,21 +79,30 @@ type Converter struct { // NewConverter creates a new Converter object. func NewConverter() *Converter { c := &Converter{ - conversionFuncs: map[typePair]reflect.Value{}, - generatedConversionFuncs: map[typePair]reflect.Value{}, - defaultingFuncs: map[reflect.Type]reflect.Value{}, - defaultingInterfaces: map[reflect.Type]interface{}{}, + conversionFuncs: NewConversionFuncs(), + generatedConversionFuncs: NewConversionFuncs(), + ignoredConversions: make(map[typePair]struct{}), + defaultingFuncs: make(map[reflect.Type]reflect.Value), + defaultingInterfaces: make(map[reflect.Type]interface{}), nameFunc: func(t reflect.Type) string { return t.Name() }, - structFieldDests: map[typeNamePair][]typeNamePair{}, - structFieldSources: map[typeNamePair][]typeNamePair{}, + structFieldDests: make(map[typeNamePair][]typeNamePair), + structFieldSources: make(map[typeNamePair][]typeNamePair), - inputFieldMappingFuncs: map[reflect.Type]FieldMappingFunc{}, - inputDefaultFlags: map[reflect.Type]FieldMatchingFlags{}, + inputFieldMappingFuncs: make(map[reflect.Type]FieldMappingFunc), + inputDefaultFlags: make(map[reflect.Type]FieldMatchingFlags), } c.RegisterConversionFunc(ByteSliceCopy) return c } +// WithConversions returns a Converter that is a copy of c but with the additional +// fns merged on top. +func (c *Converter) WithConversions(fns ConversionFuncs) *Converter { + copied := *c + copied.conversionFuncs = c.conversionFuncs.Merge(fns) + return &copied +} + // ByteSliceCopy prevents recursing into every byte func ByteSliceCopy(in *[]byte, out *[]byte, s Scope) error { *out = make([]byte, len(*in)) @@ -130,6 +142,42 @@ type Scope interface { // the value of the source or destination struct tags. type FieldMappingFunc func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string) +func NewConversionFuncs() ConversionFuncs { + return ConversionFuncs{fns: make(map[typePair]reflect.Value)} +} + +type ConversionFuncs struct { + fns map[typePair]reflect.Value +} + +// Add adds the provided conversion functions to the lookup table - they must have the signature +// `func(type1, type2, Scope) error`. Functions are added in the order passed and will override +// previously registered pairs. +func (c ConversionFuncs) Add(fns ...interface{}) error { + for _, fn := range fns { + fv := reflect.ValueOf(fn) + ft := fv.Type() + if err := verifyConversionFunctionSignature(ft); err != nil { + return err + } + c.fns[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv + } + return nil +} + +// Merge returns a new ConversionFuncs that contains all conversions from +// both other and c, with other conversions taking precedence. +func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs { + merged := NewConversionFuncs() + for k, v := range c.fns { + merged.fns[k] = v + } + for k, v := range other.fns { + merged.fns[k] = v + } + return merged +} + // Meta is supplied by Scheme, when it calls Convert. type Meta struct { SrcVersion string @@ -296,34 +344,44 @@ func verifyConversionFunctionSignature(ft reflect.Type) error { // return nil // }) func (c *Converter) RegisterConversionFunc(conversionFunc interface{}) error { - fv := reflect.ValueOf(conversionFunc) - ft := fv.Type() - if err := verifyConversionFunctionSignature(ft); err != nil { - return err - } - c.conversionFuncs[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv - return nil + return c.conversionFuncs.Add(conversionFunc) } // Similar to RegisterConversionFunc, but registers conversion function that were // automatically generated. func (c *Converter) RegisterGeneratedConversionFunc(conversionFunc interface{}) error { - fv := reflect.ValueOf(conversionFunc) - ft := fv.Type() - if err := verifyConversionFunctionSignature(ft); err != nil { - return err + return c.generatedConversionFuncs.Add(conversionFunc) +} + +// RegisterIgnoredConversion registers a "no-op" for conversion, where any requested +// conversion between from and to is ignored. +func (c *Converter) RegisterIgnoredConversion(from, to interface{}) error { + typeFrom := reflect.TypeOf(from) + typeTo := reflect.TypeOf(to) + if reflect.TypeOf(from).Kind() != reflect.Ptr { + return fmt.Errorf("expected pointer arg for 'from' param 0, got: %v", typeFrom) } - c.generatedConversionFuncs[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv + if typeTo.Kind() != reflect.Ptr { + return fmt.Errorf("expected pointer arg for 'to' param 1, got: %v", typeTo) + } + c.ignoredConversions[typePair{typeFrom.Elem(), typeTo.Elem()}] = struct{}{} return nil } +// IsConversionIgnored returns true if the specified objects should be dropped during +// conversion. +func (c *Converter) IsConversionIgnored(inType, outType reflect.Type) bool { + _, found := c.ignoredConversions[typePair{inType, outType}] + return found +} + func (c *Converter) HasConversionFunc(inType, outType reflect.Type) bool { - _, found := c.conversionFuncs[typePair{inType, outType}] + _, found := c.conversionFuncs.fns[typePair{inType, outType}] return found } func (c *Converter) ConversionFuncValue(inType, outType reflect.Type) (reflect.Value, bool) { - value, found := c.conversionFuncs[typePair{inType, outType}] + value, found := c.conversionFuncs.fns[typePair{inType, outType}] return value, found } @@ -509,16 +567,26 @@ func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error { fv.Call(args) } + pair := typePair{st, dt} + + // ignore conversions of this type + if _, ok := c.ignoredConversions[pair]; ok { + if c.Debug != nil { + c.Debug.Logf("Ignoring conversion of '%v' to '%v'", st, dt) + } + return nil + } + // Convert sv to dv. - if fv, ok := c.conversionFuncs[typePair{st, dt}]; ok { + if fv, ok := c.conversionFuncs.fns[pair]; ok { if c.Debug != nil { c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt) } return c.callCustom(sv, dv, fv, scope) } - if fv, ok := c.generatedConversionFuncs[typePair{st, dt}]; ok { + if fv, ok := c.generatedConversionFuncs.fns[pair]; ok { if c.Debug != nil { - c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt) + c.Debug.Logf("Calling generated conversion of '%v' to '%v'", st, dt) } return c.callCustom(sv, dv, fv, scope) } diff --git a/pkg/conversion/converter_test.go b/pkg/conversion/converter_test.go index 6ef60347abc..8787ec37454 100644 --- a/pkg/conversion/converter_test.go +++ b/pkg/conversion/converter_test.go @@ -24,6 +24,8 @@ import ( "testing" "github.com/google/gofuzz" + + "k8s.io/kubernetes/pkg/api/unversioned" ) func testLogger(t *testing.T) DebugLogger { @@ -221,6 +223,55 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) { } } +func TestConverter_IgnoredConversion(t *testing.T) { + type A struct{} + type B struct{} + + count := 0 + c := NewConverter() + if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { + count++ + return nil + }); err != nil { + t.Fatalf("unexpected error %v", err) + } + if err := c.RegisterIgnoredConversion(&A{}, &B{}); err != nil { + t.Fatal(err) + } + a := A{} + b := B{} + if err := c.Convert(&a, &b, 0, nil); err != nil { + t.Errorf("%v", err) + } + if count != 0 { + t.Errorf("unexpected number of conversion invocations") + } +} + +func TestConverter_IgnoredConversionNested(t *testing.T) { + type C string + type A struct { + C C + } + type B struct { + C C + } + + c := NewConverter() + typed := C("") + if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil { + t.Fatal(err) + } + a := A{C: C("test")} + b := B{C: C("other")} + if err := c.Convert(&a, &b, AllowDifferentFieldTypeNames, nil); err != nil { + t.Errorf("%v", err) + } + if b.C != C("other") { + t.Errorf("expected no conversion of field C: %#v", b) + } +} + func TestConverter_GeneratedConversionOverriden(t *testing.T) { type A struct{} type B struct{} @@ -243,6 +294,37 @@ func TestConverter_GeneratedConversionOverriden(t *testing.T) { } } +func TestConverter_WithConversionOverriden(t *testing.T) { + type A struct{} + type B struct{} + c := NewConverter() + if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error { + return fmt.Errorf("conversion function should be overriden") + }); err != nil { + t.Fatalf("unexpected error %v", err) + } + if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error { + return fmt.Errorf("generated function should be overriden") + }); err != nil { + t.Fatalf("unexpected error %v", err) + } + + ext := NewConversionFuncs() + ext.Add(func(in *A, out *B, s Scope) error { + return nil + }) + newc := c.WithConversions(ext) + + a := A{} + b := B{} + if err := c.Convert(&a, &b, 0, nil); err == nil || err.Error() != "conversion function should be overriden" { + t.Errorf("unexpected error: %v", err) + } + if err := newc.Convert(&a, &b, 0, nil); err != nil { + t.Errorf("%v", err) + } +} + func TestConverter_MapsStringArrays(t *testing.T) { type A struct { Foo string @@ -681,3 +763,132 @@ func TestConverter_FieldRename(t *testing.T) { } } } + +func TestMetaValues(t *testing.T) { + type InternalSimple struct { + APIVersion string `json:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty"` + TestString string `json:"testString"` + } + type ExternalSimple struct { + APIVersion string `json:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty"` + TestString string `json:"testString"` + } + internalGV := unversioned.GroupVersion{Group: "test.group", Version: "__internal"} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"} + + s := NewScheme() + s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) + s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) + + internalToExternalCalls := 0 + externalToInternalCalls := 0 + + // Register functions to verify that scope.Meta() gets set correctly. + err := s.AddConversionFuncs( + func(in *InternalSimple, out *ExternalSimple, scope Scope) error { + t.Logf("internal -> external") + if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a { + t.Fatalf("Expected '%v', got '%v'", e, a) + } + if e, a := externalGV.String(), scope.Meta().DestVersion; e != a { + t.Fatalf("Expected '%v', got '%v'", e, a) + } + scope.Convert(&in.TestString, &out.TestString, 0) + internalToExternalCalls++ + return nil + }, + func(in *ExternalSimple, out *InternalSimple, scope Scope) error { + t.Logf("external -> internal") + if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a { + t.Errorf("Expected '%v', got '%v'", e, a) + } + if e, a := internalGV.String(), scope.Meta().DestVersion; e != a { + t.Fatalf("Expected '%v', got '%v'", e, a) + } + scope.Convert(&in.TestString, &out.TestString, 0) + externalToInternalCalls++ + return nil + }, + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + simple := &InternalSimple{ + TestString: "foo", + } + + s.Log(t) + + out, err := s.ConvertToVersion(simple, externalGV.String()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + internal, err := s.ConvertToVersion(out, internalGV.String()) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if e, a := simple, internal; !reflect.DeepEqual(e, a) { + t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) + } + + if e, a := 1, internalToExternalCalls; e != a { + t.Errorf("Expected %v, got %v", e, a) + } + if e, a := 1, externalToInternalCalls; e != a { + t.Errorf("Expected %v, got %v", e, a) + } +} + +func TestMetaValuesUnregisteredConvert(t *testing.T) { + type InternalSimple struct { + Version string `json:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty"` + TestString string `json:"testString"` + } + type ExternalSimple struct { + Version string `json:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty"` + TestString string `json:"testString"` + } + s := NewScheme() + // We deliberately don't register the types. + + internalToExternalCalls := 0 + + // Register functions to verify that scope.Meta() gets set correctly. + err := s.AddConversionFuncs( + func(in *InternalSimple, out *ExternalSimple, scope Scope) error { + if e, a := "unknown/unknown", scope.Meta().SrcVersion; e != a { + t.Fatalf("Expected '%v', got '%v'", e, a) + } + if e, a := "unknown/unknown", scope.Meta().DestVersion; e != a { + t.Fatalf("Expected '%v', got '%v'", e, a) + } + scope.Convert(&in.TestString, &out.TestString, 0) + internalToExternalCalls++ + return nil + }, + ) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + simple := &InternalSimple{TestString: "foo"} + external := &ExternalSimple{} + err = s.Convert(simple, external) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if e, a := simple.TestString, external.TestString; e != a { + t.Errorf("Expected %v, got %v", e, a) + } + + // Verify that our conversion handler got called. + if e, a := 1, internalToExternalCalls; e != a { + t.Errorf("Expected %v, got %v", e, a) + } +} diff --git a/pkg/conversion/decode.go b/pkg/conversion/decode.go deleted file mode 100644 index 0e5c7a76285..00000000000 --- a/pkg/conversion/decode.go +++ /dev/null @@ -1,194 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package conversion - -import ( - "errors" - "fmt" - "net/url" - - "github.com/ugorji/go/codec" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -func (s *Scheme) DecodeToVersionedObject(data []byte) (interface{}, unversioned.GroupVersionKind, error) { - kind, err := s.DataKind(data) - if err != nil { - return nil, unversioned.GroupVersionKind{}, err - } - - internalGV, exists := s.InternalVersions[kind.Group] - if !exists { - return nil, unversioned.GroupVersionKind{}, fmt.Errorf("no internalVersion specified for %v", kind) - } - - if len(kind.Group) == 0 && len(internalGV.Group) != 0 { - return nil, unversioned.GroupVersionKind{}, fmt.Errorf("group not set in '%s'", string(data)) - } - if len(kind.Version) == 0 && len(internalGV.Version) != 0 { - return nil, unversioned.GroupVersionKind{}, fmt.Errorf("version not set in '%s'", string(data)) - } - if kind.Kind == "" { - return nil, unversioned.GroupVersionKind{}, fmt.Errorf("kind not set in '%s'", string(data)) - } - - obj, err := s.NewObject(kind) - if err != nil { - return nil, unversioned.GroupVersionKind{}, err - } - - if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(obj); err != nil { - return nil, unversioned.GroupVersionKind{}, err - } - return obj, kind, nil -} - -// Decode converts a JSON string back into a pointer to an api object. -// Deduces the type based upon the fields added by the MetaInsertionFactory -// technique. The object will be converted, if necessary, into the -// s.InternalVersion type before being returned. Decode will not decode -// objects without version set unless InternalVersion is also "". -func (s *Scheme) Decode(data []byte) (interface{}, error) { - return s.DecodeToVersion(data, unversioned.GroupVersion{}) -} - -// DecodeToVersion converts a JSON string back into a pointer to an api object. -// Deduces the type based upon the fields added by the MetaInsertionFactory -// technique. The object will be converted, if necessary, into the versioned -// type before being returned. Decode will not decode objects without version -// set unless version is also "". -// a GroupVersion with .IsEmpty() == true is means "use the internal version for -// the object's group" -func (s *Scheme) DecodeToVersion(data []byte, targetVersion unversioned.GroupVersion) (interface{}, error) { - obj, sourceKind, err := s.DecodeToVersionedObject(data) - if err != nil { - return nil, err - } - // Version and Kind should be blank in memory. - if err := s.SetVersionAndKind("", "", obj); err != nil { - return nil, err - } - - // if the targetVersion is empty, then we want the internal version, but the internal version varies by - // group. We can lookup the group now because we have knowledge of the group - if targetVersion.IsEmpty() { - exists := false - targetVersion, exists = s.InternalVersions[sourceKind.Group] - if !exists { - return nil, fmt.Errorf("no internalVersion specified for %v", targetVersion) - } - } - - // Convert if needed. - if targetVersion != sourceKind.GroupVersion() { - objOut, err := s.NewObject(targetVersion.WithKind(sourceKind.Kind)) - if err != nil { - return nil, err - } - flags, meta := s.generateConvertMeta(sourceKind.GroupVersion(), targetVersion, obj) - if err := s.converter.Convert(obj, objOut, flags, meta); err != nil { - return nil, err - } - obj = objOut - } - return obj, nil -} - -// DecodeInto parses a JSON string and stores it in obj. Returns an error -// if data.Kind is set and doesn't match the type of obj. Obj should be a -// pointer to an api type. -// If obj's version doesn't match that in data, an attempt will be made to convert -// data into obj's version. -func (s *Scheme) DecodeInto(data []byte, obj interface{}) error { - return s.DecodeIntoWithSpecifiedVersionKind(data, obj, unversioned.GroupVersionKind{}) -} - -// DecodeIntoWithSpecifiedVersionKind compares the passed in requestGroupVersionKind -// with data.Version and data.Kind, defaulting data.Version and -// data.Kind to the specified value if they are empty, or generating an error if -// data.Version and data.Kind are not empty and differ from the specified value. -// The function then implements the functionality of DecodeInto. -// If specifiedVersion and specifiedKind are empty, the function degenerates to -// DecodeInto. -func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}, requestedGVK unversioned.GroupVersionKind) error { - if len(data) == 0 { - return errors.New("empty input") - } - dataKind, err := s.DataKind(data) - if err != nil { - return err - } - if len(dataKind.Group) == 0 { - dataKind.Group = requestedGVK.Group - } - if len(dataKind.Version) == 0 { - dataKind.Version = requestedGVK.Version - } - if len(dataKind.Kind) == 0 { - dataKind.Kind = requestedGVK.Kind - } - - if len(requestedGVK.Group) > 0 && requestedGVK.Group != dataKind.Group { - return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataKind, requestedGVK)) - } - if len(requestedGVK.Version) > 0 && requestedGVK.Version != dataKind.Version { - return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataKind, requestedGVK)) - } - if len(requestedGVK.Kind) > 0 && requestedGVK.Kind != dataKind.Kind { - return errors.New(fmt.Sprintf("The fully qualified kind in the data (%v) does not match the specified apiVersion(%v)", dataKind, requestedGVK)) - } - - objGVK, err := s.ObjectKind(obj) - if err != nil { - return err - } - // Assume objects with unset fields are being unmarshalled into the - // correct type. - if len(dataKind.Group) == 0 { - dataKind.Group = objGVK.Group - } - if len(dataKind.Version) == 0 { - dataKind.Version = objGVK.Version - } - if len(dataKind.Kind) == 0 { - dataKind.Kind = objGVK.Kind - } - - external, err := s.NewObject(dataKind) - if err != nil { - return err - } - if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(external); err != nil { - return err - } - flags, meta := s.generateConvertMeta(dataKind.GroupVersion(), objGVK.GroupVersion(), external) - if err := s.converter.Convert(external, obj, flags, meta); err != nil { - return err - } - - // Version and Kind should be blank in memory. - return s.SetVersionAndKind("", "", obj) -} - -func (s *Scheme) DecodeParametersInto(parameters url.Values, obj interface{}) error { - if err := s.Convert(¶meters, obj); err != nil { - return err - } - // TODO: Should we do any convertion here? - return nil -} diff --git a/pkg/conversion/doc.go b/pkg/conversion/doc.go index f0626a569c2..3ef2eaba457 100644 --- a/pkg/conversion/doc.go +++ b/pkg/conversion/doc.go @@ -14,18 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package conversion provides go object versioning and encoding/decoding -// mechanisms. +// Package conversion provides go object versioning. // // Specifically, conversion provides a way for you to define multiple versions // of the same object. You may write functions which implement conversion logic, // but for the fields which did not change, copying is automated. This makes it // easy to modify the structures you use in memory without affecting the format // you store on disk or respond to in your external API calls. -// -// The second offering of this package is automated encoding/decoding. The version -// and type of the object is recorded in the output, so it can be recreated upon -// reading. Currently, conversion writes JSON output, and interprets both JSON -// and YAML input. -// package conversion diff --git a/pkg/conversion/encode.go b/pkg/conversion/encode.go deleted file mode 100644 index 3120583d454..00000000000 --- a/pkg/conversion/encode.go +++ /dev/null @@ -1,155 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package conversion - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "path" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -// EncodeToVersion turns the given api object into an appropriate JSON string. -// Obj may be a pointer to a struct, or a struct. If a struct, a copy -// will be made, therefore it's recommended to pass a pointer to a -// struct. The type must have been registered. -// -// Memory/wire format differences: -// * Having to keep track of the Kind and Version fields makes tests -// very annoying, so the rule is that they are set only in wire format -// (json), not when in native (memory) format. This is possible because -// both pieces of information are implicit in the go typed object. -// * An exception: note that, if there are embedded API objects of known -// type, for example, PodList{... Items []Pod ...}, these embedded -// objects must be of the same version of the object they are embedded -// within, and their Version and Kind must both be empty. -// * Note that the exception does not apply to a generic APIObject type -// which recursively does Encode()/Decode(), and is capable of -// expressing any API object. -// * Only versioned objects should be encoded. This means that, if you pass -// a native object, Encode will convert it to a versioned object. For -// example, an api.Pod will get converted to a v1.Pod. However, if -// you pass in an object that's already versioned (v1.Pod), Encode -// will not modify it. -// -// The purpose of the above complex conversion behavior is to allow us to -// change the memory format yet not break compatibility with any stored -// objects, whether they be in our storage layer (e.g., etcd), or in user's -// config files. -// -func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []byte, err error) { - buff := &bytes.Buffer{} - if err := s.EncodeToVersionStream(obj, destVersion, buff); err != nil { - return nil, err - } - return buff.Bytes(), nil -} - -func (s *Scheme) EncodeToVersionStream(obj interface{}, destGroupVersionString string, stream io.Writer) error { - obj = maybeCopy(obj) - v, _ := EnforcePtr(obj) // maybeCopy guarantees a pointer - - // Don't encode an object defined in the unversioned package, unless if the - // destGroupVersionString is v1, encode it to v1 for backward compatibility. - pkg := path.Base(v.Type().PkgPath()) - if pkg == "unversioned" && destGroupVersionString != "v1" { - // TODO: convert this to streaming too - data, err := s.encodeUnversionedObject(obj) - if err != nil { - return err - } - _, err = stream.Write(data) - return err - } - - if _, registered := s.typeToGVK[v.Type()]; !registered { - return fmt.Errorf("type %v is not registered for %q and it will be impossible to Decode it, therefore Encode will refuse to encode it.", v.Type(), destGroupVersionString) - } - - objKind, err := s.ObjectKind(obj) - if err != nil { - return err - } - - destVersion, err := unversioned.ParseGroupVersion(destGroupVersionString) - if err != nil { - return err - } - - // Perform a conversion if necessary. - if objKind.GroupVersion() != destVersion { - objOut, err := s.NewObject(destVersion.WithKind(objKind.Kind)) - if err != nil { - return err - } - flags, meta := s.generateConvertMeta(objKind.GroupVersion(), destVersion, obj) - err = s.converter.Convert(obj, objOut, flags, meta) - if err != nil { - return err - } - obj = objOut - - // ensure the output object name comes from the destination type - newGroupVersionKind, err := s.ObjectKind(obj) - if err != nil { - return err - } - objKind.Kind = newGroupVersionKind.Kind - } - - // Version and Kind should be set on the wire. - err = s.SetVersionAndKind(destVersion.String(), objKind.Kind, obj) - if err != nil { - return err - } - - // To add metadata, do some simple surgery on the JSON. - encoder := json.NewEncoder(stream) - if err := encoder.Encode(obj); err != nil { - return err - } - - // Version and Kind should be blank in memory. Reset them, since it's - // possible that we modified a user object and not a copy above. - err = s.SetVersionAndKind("", "", obj) - if err != nil { - return err - } - - return nil -} - -func (s *Scheme) encodeUnversionedObject(obj interface{}) (data []byte, err error) { - objGVK, err := s.ObjectKind(obj) - if err != nil { - return nil, err - } - if err = s.SetVersionAndKind("", objGVK.Kind, obj); err != nil { - return nil, err - } - data, err = json.Marshal(obj) - if err != nil { - return nil, err - } - // Version and Kind should be blank in memory. Reset them, since it's - // possible that we modified a user object and not a copy above. - err = s.SetVersionAndKind("", "", obj) - return data, nil -} diff --git a/pkg/conversion/error.go b/pkg/conversion/error.go index 6ddbf9b7a78..94075e8adfb 100644 --- a/pkg/conversion/error.go +++ b/pkg/conversion/error.go @@ -28,6 +28,11 @@ type notRegisteredErr struct { t reflect.Type } +// NewNotRegisteredErr is exposed for testing. +func NewNotRegisteredErr(gvk unversioned.GroupVersionKind, t reflect.Type) error { + return ¬RegisteredErr{gvk: gvk, t: t} +} + func (k *notRegisteredErr) Error() string { if k.t != nil { return fmt.Sprintf("no kind is registered for the type %v", k.t) diff --git a/pkg/conversion/helper.go b/pkg/conversion/helper.go new file mode 100644 index 00000000000..39f78265959 --- /dev/null +++ b/pkg/conversion/helper.go @@ -0,0 +1,39 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package conversion + +import ( + "fmt" + "reflect" +) + +// EnforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value +// of the dereferenced pointer, ensuring that it is settable/addressable. +// Returns an error if this is not possible. +func EnforcePtr(obj interface{}) (reflect.Value, error) { + v := reflect.ValueOf(obj) + if v.Kind() != reflect.Ptr { + if v.Kind() == reflect.Invalid { + return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind") + } + return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type()) + } + if v.IsNil() { + return reflect.Value{}, fmt.Errorf("expected pointer, but got nil") + } + return v.Elem(), nil +} diff --git a/pkg/apis/componentconfig/decode.go b/pkg/conversion/helper_test.go similarity index 54% rename from pkg/apis/componentconfig/decode.go rename to pkg/conversion/helper_test.go index 7fe927c3272..69fef3334b1 100644 --- a/pkg/apis/componentconfig/decode.go +++ b/pkg/conversion/helper_test.go @@ -1,5 +1,5 @@ /* -Copyright 2015 The Kubernetes Authors All rights reserved. +Copyright 2014 The Kubernetes Authors All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,20 +14,25 @@ See the License for the specific language governing permissions and limitations under the License. */ -package componentconfig +package conversion -import ( - "io/ioutil" +import "testing" - "k8s.io/kubernetes/pkg/runtime" -) - -func DecodeFromPathInto(obj runtime.Object, c runtime.Codec, filename string) error { - b, err := ioutil.ReadFile(filename) - c = runtime.YAMLDecoder(c) - - if err != nil { - return err +func TestInvalidPtrValueKind(t *testing.T) { + var simple interface{} + switch obj := simple.(type) { + default: + _, err := EnforcePtr(obj) + if err == nil { + t.Errorf("Expected error on invalid kind") + } + } +} + +func TestEnforceNilPtr(t *testing.T) { + var nilPtr *struct{} + _, err := EnforcePtr(nilPtr) + if err == nil { + t.Errorf("Expected error on nil pointer") } - return c.DecodeInto(b, obj) } diff --git a/pkg/conversion/meta.go b/pkg/conversion/meta.go deleted file mode 100644 index bb33d12cb56..00000000000 --- a/pkg/conversion/meta.go +++ /dev/null @@ -1,153 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package conversion - -import ( - "encoding/json" - "fmt" - "path" - "reflect" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -// MetaFactory is used to store and retrieve the version and kind -// information for all objects in a scheme. -type MetaFactory interface { - // Update sets the given version and kind onto the object. - Update(version, kind string, obj interface{}) error - // Interpret should return the group,version,kind of the wire-format of - // the object. - Interpret(data []byte) (gvk unversioned.GroupVersionKind, err error) -} - -// DefaultMetaFactory is a default factory for versioning objects in JSON. The object -// in memory and in the default JSON serialization will use the "kind" and "apiVersion" -// fields. -var DefaultMetaFactory = SimpleMetaFactory{KindField: "Kind", VersionField: "APIVersion"} - -// SimpleMetaFactory provides default methods for retrieving the type and version of objects -// that are identified with an "apiVersion" and "kind" fields in their JSON -// serialization. It may be parameterized with the names of the fields in memory, or an -// optional list of base structs to search for those fields in memory. -type SimpleMetaFactory struct { - // The name of the API version field in memory of the struct - VersionField string - // The name of the kind field in memory of the struct. - KindField string - // Optional, if set will look in the named inline structs to find the fields to set. - BaseFields []string -} - -// Interpret will return the group,version,kind of the JSON wire-format -// encoding of an object, or an error. -func (SimpleMetaFactory) Interpret(data []byte) (unversioned.GroupVersionKind, error) { - findKind := struct { - APIVersion string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - }{} - err := json.Unmarshal(data, &findKind) - if err != nil { - return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't get version/kind; json parse error: %v", err) - } - gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) - if err != nil { - return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't parse apiVersion: %v", err) - } - - return gv.WithKind(findKind.Kind), nil -} - -func (f SimpleMetaFactory) Update(version, kind string, obj interface{}) error { - return UpdateVersionAndKind(f.BaseFields, f.VersionField, version, f.KindField, kind, obj) -} - -// UpdateVersionAndKind uses reflection to find and set the versionField and kindField fields -// on a pointer to a struct to version and kind. Provided as a convenience for others -// implementing MetaFactory. Pass an array to baseFields to check one or more nested structs -// for the named fields. The version field is treated as optional if it is not present in the struct. -// TODO: this method is on its way out -func UpdateVersionAndKind(baseFields []string, versionField, version, kindField, kind string, obj interface{}) error { - if typed, ok := obj.(unversioned.ObjectKind); ok { - if len(version) == 0 && len(kind) == 0 { - typed.SetGroupVersionKind(nil) - } else { - gv, err := unversioned.ParseGroupVersion(version) - if err != nil { - return err - } - typed.SetGroupVersionKind(&unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}) - } - return nil - } - v, err := EnforcePtr(obj) - if err != nil { - return err - } - pkg := path.Base(v.Type().PkgPath()) - t := v.Type() - name := t.Name() - if v.Kind() != reflect.Struct { - return fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), name, v.Interface()) - } - - for i := range baseFields { - base := v.FieldByName(baseFields[i]) - if !base.IsValid() { - continue - } - v = base - } - - field := v.FieldByName(kindField) - if !field.IsValid() { - // Types defined in the unversioned package are allowed to not have a - // kindField. Clients will have to know what they are based on the - // context. - // TODO: add some type trait here, or some way of indicating whether - // this feature is allowed on a per-type basis. Using package name is - // overly broad and a bit hacky. - if pkg == "unversioned" { - return nil - } - return fmt.Errorf("couldn't find %v field in %#v", kindField, v.Interface()) - } - field.SetString(kind) - - if field := v.FieldByName(versionField); field.IsValid() { - field.SetString(version) - } - - return nil -} - -// EnforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value -// of the dereferenced pointer, ensuring that it is settable/addressable. -// Returns an error if this is not possible. -func EnforcePtr(obj interface{}) (reflect.Value, error) { - v := reflect.ValueOf(obj) - if v.Kind() != reflect.Ptr { - if v.Kind() == reflect.Invalid { - return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind") - } - return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type()) - } - if v.IsNil() { - return reflect.Value{}, fmt.Errorf("expected pointer, but got nil") - } - return v.Elem(), nil -} diff --git a/pkg/conversion/meta_test.go b/pkg/conversion/meta_test.go deleted file mode 100644 index 4cf92fb0688..00000000000 --- a/pkg/conversion/meta_test.go +++ /dev/null @@ -1,289 +0,0 @@ -/* -Copyright 2014 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package conversion - -import ( - "fmt" - "reflect" - "testing" - - "k8s.io/kubernetes/pkg/api/unversioned" -) - -func TestSimpleMetaFactoryInterpret(t *testing.T) { - factory := SimpleMetaFactory{} - fqKind, err := factory.Interpret([]byte(`{"apiVersion":"g/1","kind":"object"}`)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expectedFQKind := unversioned.GroupVersionKind{Group: "g", Version: "1", Kind: "object"} - if expectedFQKind != fqKind { - t.Errorf("unexpected interpret: %s %s", expectedFQKind, fqKind) - } - - // no kind or version - fqKind, err = factory.Interpret([]byte(`{}`)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !fqKind.IsEmpty() { - t.Errorf("unexpected interpret: %s %s", fqKind) - } - - // unparsable - fqKind, err = factory.Interpret([]byte(`{`)) - if err == nil { - t.Errorf("unexpected non-error") - } -} - -func TestSimpleMetaFactoryUpdate(t *testing.T) { - factory := SimpleMetaFactory{VersionField: "V", KindField: "K"} - - obj := struct { - V string - K string - }{"1", "2"} - - // must pass a pointer - if err := factory.Update("test", "other", obj); err == nil { - t.Errorf("unexpected non-error") - } - if obj.V != "1" || obj.K != "2" { - t.Errorf("unexpected update: %v", obj) - } - - // updates - if err := factory.Update("test", "other", &obj); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if obj.V != "test" || obj.K != "other" { - t.Errorf("unexpected update: %v", obj) - } -} - -// Test Updating objects that don't have a Kind field. -func TestSimpleMetaFactoryUpdateNoKindField(t *testing.T) { - factory := SimpleMetaFactory{VersionField: "APIVersion", KindField: "Kind"} - // obj does not have a Kind field and is not defined in the unversioned package. - obj := struct { - SomeField string - }{"1"} - expectedError := fmt.Errorf("couldn't find %v field in %#v", factory.KindField, obj) - if err := factory.Update("test", "other", &obj); err == nil || expectedError.Error() != err.Error() { - t.Fatalf("expected error: %v, got: %v", expectedError, err) - } - - // ListMeta does not have a Kind field, but is defined in the unversioned package. - listMeta := unversioned.ListMeta{} - if err := factory.Update("test", "other", &listMeta); err != nil { - t.Fatalf("unexpected error: %v", err) - } -} - -func TestSimpleMetaFactoryUpdateStruct(t *testing.T) { - factory := SimpleMetaFactory{BaseFields: []string{"Test"}, VersionField: "V", KindField: "K"} - - type Inner struct { - V string - K string - } - obj := struct { - Test Inner - }{Test: Inner{"1", "2"}} - - // updates - if err := factory.Update("test", "other", &obj); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if obj.Test.V != "test" || obj.Test.K != "other" { - t.Errorf("unexpected update: %v", obj) - } -} - -func TestMetaValues(t *testing.T) { - type InternalSimple struct { - APIVersion string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - TestString string `json:"testString"` - } - type ExternalSimple struct { - APIVersion string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - TestString string `json:"testString"` - } - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} - externalGV := unversioned.GroupVersion{Group: "test.group", Version: "externalVersion"} - - s := NewScheme() - s.InternalVersions[internalGV.Group] = internalGV - s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) - s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) - - internalToExternalCalls := 0 - externalToInternalCalls := 0 - - // Register functions to verify that scope.Meta() gets set correctly. - err := s.AddConversionFuncs( - func(in *InternalSimple, out *ExternalSimple, scope Scope) error { - t.Logf("internal -> external") - if e, a := internalGV.String(), scope.Meta().SrcVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - if e, a := externalGV.String(), scope.Meta().DestVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - internalToExternalCalls++ - return nil - }, - func(in *ExternalSimple, out *InternalSimple, scope Scope) error { - t.Logf("external -> internal") - if e, a := externalGV.String(), scope.Meta().SrcVersion; e != a { - t.Errorf("Expected '%v', got '%v'", e, a) - } - if e, a := internalGV.String(), scope.Meta().DestVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - externalToInternalCalls++ - return nil - }, - ) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - simple := &InternalSimple{ - TestString: "foo", - } - - s.Log(t) - - // Test Encode, Decode, and DecodeInto - data, err := s.EncodeToVersion(simple, externalGV.String()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - t.Logf(string(data)) - obj2, err := s.Decode(data) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if _, ok := obj2.(*InternalSimple); !ok { - t.Fatalf("Got wrong type") - } - if e, a := simple, obj2; !reflect.DeepEqual(e, a) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) - } - - obj3 := &InternalSimple{} - if err := s.DecodeInto(data, obj3); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if e, a := simple, obj3; !reflect.DeepEqual(e, a) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) - } - - // Test Convert - external := &ExternalSimple{} - err = s.Convert(simple, external) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if e, a := simple.TestString, external.TestString; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - - // Encode and Convert should each have caused an increment. - if e, a := 2, internalToExternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - // Decode and DecodeInto should each have caused an increment. - if e, a := 2, externalToInternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } -} - -func TestMetaValuesUnregisteredConvert(t *testing.T) { - type InternalSimple struct { - Version string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - TestString string `json:"testString"` - } - type ExternalSimple struct { - Version string `json:"apiVersion,omitempty"` - Kind string `json:"kind,omitempty"` - TestString string `json:"testString"` - } - s := NewScheme() - // We deliberately don't register the types. - - internalToExternalCalls := 0 - - // Register functions to verify that scope.Meta() gets set correctly. - err := s.AddConversionFuncs( - func(in *InternalSimple, out *ExternalSimple, scope Scope) error { - if e, a := "unknown/unknown", scope.Meta().SrcVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - if e, a := "unknown/unknown", scope.Meta().DestVersion; e != a { - t.Fatalf("Expected '%v', got '%v'", e, a) - } - scope.Convert(&in.TestString, &out.TestString, 0) - internalToExternalCalls++ - return nil - }, - ) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - simple := &InternalSimple{TestString: "foo"} - external := &ExternalSimple{} - err = s.Convert(simple, external) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - if e, a := simple.TestString, external.TestString; e != a { - t.Errorf("Expected %v, got %v", e, a) - } - - // Verify that our conversion handler got called. - if e, a := 1, internalToExternalCalls; e != a { - t.Errorf("Expected %v, got %v", e, a) - } -} - -func TestInvalidPtrValueKind(t *testing.T) { - var simple interface{} - switch obj := simple.(type) { - default: - _, err := EnforcePtr(obj) - if err == nil { - t.Errorf("Expected error on invalid kind") - } - } -} - -func TestEnforceNilPtr(t *testing.T) { - var nilPtr *struct{} - _, err := EnforcePtr(nilPtr) - if err == nil { - t.Errorf("Expected error on nil pointer") - } -} diff --git a/pkg/conversion/queryparams/convert.go b/pkg/conversion/queryparams/convert.go index 450a43001a2..0f04e7bb4cb 100644 --- a/pkg/conversion/queryparams/convert.go +++ b/pkg/conversion/queryparams/convert.go @@ -21,8 +21,6 @@ import ( "net/url" "reflect" "strings" - - "k8s.io/kubernetes/pkg/runtime" ) func jsonTag(field reflect.StructField) (string, bool) { @@ -93,10 +91,10 @@ func addListOfParams(values url.Values, tag string, omitempty bool, list reflect } } -// Convert takes a versioned runtime.Object and serializes it to a url.Values object -// using JSON tags as parameter names. Only top-level simple values, arrays, and slices -// are serialized. Embedded structs, maps, etc. will not be serialized. -func Convert(obj runtime.Object) (url.Values, error) { +// Convert takes an object and converts it to a url.Values object using JSON tags as +// parameter names. Only top-level simple values, arrays, and slices are serialized. +// Embedded structs, maps, etc. will not be serialized. +func Convert(obj interface{}) (url.Values, error) { result := url.Values{} if obj == nil { return result, nil diff --git a/pkg/conversion/queryparams/convert_test.go b/pkg/conversion/queryparams/convert_test.go index 8b513f076db..405357557fa 100644 --- a/pkg/conversion/queryparams/convert_test.go +++ b/pkg/conversion/queryparams/convert_test.go @@ -23,7 +23,6 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion/queryparams" - "k8s.io/kubernetes/pkg/runtime" ) type namedString string @@ -85,7 +84,7 @@ func validateResult(t *testing.T, input interface{}, actual, expected url.Values func TestConvert(t *testing.T) { tests := []struct { - input runtime.Object + input interface{} expected url.Values }{ { diff --git a/pkg/conversion/scheme.go b/pkg/conversion/scheme.go index e5ea10b50c1..236ac39f971 100644 --- a/pkg/conversion/scheme.go +++ b/pkg/conversion/scheme.go @@ -33,6 +33,13 @@ type Scheme struct { // The reflect.Type we index by should *not* be a pointer. typeToGVK map[reflect.Type][]unversioned.GroupVersionKind + // unversionedTypes are transformed without conversion in ConvertToVersion. + unversionedTypes map[reflect.Type]unversioned.GroupVersionKind + // unversionedKinds are the names of kinds that can be created in the context of any group + // or version + // TODO: resolve the status of unversioned types. + unversionedKinds map[string]reflect.Type + // converter stores all registered conversion functions. It also has // default coverting behavior. converter *Converter @@ -44,34 +51,17 @@ type Scheme struct { // Indent will cause the JSON output from Encode to be indented, // if and only if it is true. Indent bool - - // InternalVersion is the default internal version. It is recommended that - // you use "" for the internal version. - // TODO logically the InternalVersion is different for every Group, so this structure - // must be map - InternalVersions map[string]unversioned.GroupVersion - - // MetaInsertionFactory is used to create an object to store and retrieve - // the version and kind information for all objects. The default uses the - // keys "apiVersion" and "kind" respectively. - MetaFactory MetaFactory } // NewScheme manufactures a new scheme. func NewScheme() *Scheme { s := &Scheme{ - gvkToType: map[unversioned.GroupVersionKind]reflect.Type{}, - typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{}, - converter: NewConverter(), - cloner: NewCloner(), - // TODO remove this hard coded list. As step one, hardcode it here so this pull doesn't become even bigger - InternalVersions: map[string]unversioned.GroupVersion{ - "": {}, - "componentconfig": {Group: "componentconfig"}, - "extensions": {Group: "extensions"}, - "metrics": {Group: "metrics"}, - }, - MetaFactory: DefaultMetaFactory, + gvkToType: map[unversioned.GroupVersionKind]reflect.Type{}, + typeToGVK: map[reflect.Type][]unversioned.GroupVersionKind{}, + unversionedTypes: map[reflect.Type]unversioned.GroupVersionKind{}, + unversionedKinds: map[string]reflect.Type{}, + converter: NewConverter(), + cloner: NewCloner(), } s.converter.nameFunc = s.nameFunc return s @@ -92,11 +82,8 @@ func (s *Scheme) nameFunc(t reflect.Type) string { } for _, gvk := range gvks { - internalGV, exists := s.InternalVersions[gvk.Group] - if !exists { - internalGV := gvk.GroupVersion() - internalGV.Version = "" - } + internalGV := gvk.GroupVersion() + internalGV.Version = "__internal" // this is hacky and maybe should be passed in internalGVK := internalGV.WithKind(gvk.Kind) if internalType, exists := s.gvkToType[internalGVK]; exists { @@ -107,11 +94,30 @@ func (s *Scheme) nameFunc(t reflect.Type) string { return gvks[0].Kind } +// AddUnversionedTypes registers all types passed in 'types' as being members of version 'version', +// and marks them as being convertible to all API versions. +// All objects passed to types should be pointers to structs. The name that go reports for +// the struct becomes the "kind" field when encoding. +func (s *Scheme) AddUnversionedTypes(version unversioned.GroupVersion, types ...interface{}) { + s.AddKnownTypes(version, types...) + for _, obj := range types { + t := reflect.TypeOf(obj).Elem() + gvk := version.WithKind(t.Name()) + s.unversionedTypes[t] = gvk + if _, ok := s.unversionedKinds[gvk.Kind]; ok { + panic(fmt.Sprintf("%v has already been registered as unversioned kind %q - kind name must be unique", reflect.TypeOf(t), gvk.Kind)) + } + s.unversionedKinds[gvk.Kind] = t + } +} + // AddKnownTypes registers all types passed in 'types' as being members of version 'version'. -// Encode() will refuse objects unless their type has been registered with AddKnownTypes. // All objects passed to types should be pointers to structs. The name that go reports for // the struct becomes the "kind" field when encoding. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...interface{}) { + if len(gv.Version) == 0 { + panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0])) + } for _, obj := range types { t := reflect.TypeOf(obj) if t.Kind() != reflect.Ptr { @@ -133,6 +139,9 @@ func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...interface{} // your structs. func (s *Scheme) AddKnownTypeWithName(gvk unversioned.GroupVersionKind, obj interface{}) { t := reflect.TypeOf(obj) + if len(gvk.Version) == 0 { + panic(fmt.Sprintf("version is required on all types: %s %v", gvk, t)) + } if t.Kind() != reflect.Ptr { panic("All types must be pointers to structs.") } @@ -168,6 +177,9 @@ func (s *Scheme) NewObject(kind unversioned.GroupVersionKind) (interface{}, erro return reflect.New(t).Interface(), nil } + if t, exists := s.unversionedKinds[kind.Kind]; exists { + return reflect.New(t).Interface(), nil + } return nil, ¬RegisteredErr{gvk: kind} } @@ -221,6 +233,13 @@ func (s *Scheme) AddGeneratedConversionFuncs(conversionFuncs ...interface{}) err return nil } +// AddIgnoredConversionType identifies a pair of types that should be skipped by +// dynamic conversion (because the data inside them is explicitly dropped during +// conversion). +func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error { + return s.converter.RegisterIgnoredConversion(from, to) +} + // AddDeepCopyFuncs adds functions to the list of deep copy functions. // Note that to copy sub-objects, you can use the conversion.Cloner object that // will be passed to your deep-copy function. @@ -282,6 +301,22 @@ func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool { return exists } +// IsUnversioned returns true if the Go object is registered as an unversioned type, or sets +// ok to false if the provided object is not registered in the scheme. +func (s *Scheme) IsUnversioned(obj interface{}) (unversioned bool, registered bool) { + v, err := EnforcePtr(obj) + if err != nil { + return false, false + } + t := v.Type() + + if _, ok := s.typeToGVK[t]; !ok { + return false, false + } + _, ok := s.unversionedTypes[t] + return ok, true +} + // RegisterInputDefaults sets the provided field mapping function and field matching // as the defaults for the provided input type. The fn may be nil, in which case no // mapping will happen by default. Use this method to register a mechanism for handling @@ -330,15 +365,23 @@ func (s *Scheme) ConvertToVersion(in interface{}, outGroupVersionString string) return nil, fmt.Errorf("only pointers to struct types may be converted: %v", t) } - gvks, ok := s.typeToGVK[t] - if !ok { - return nil, fmt.Errorf("%v cannot be converted into version %q", t, outGroupVersionString) - } outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString) if err != nil { return nil, err } - outKind := outVersion.WithKind(gvks[0].Kind) + + var kind unversioned.GroupVersionKind + if unversionedKind, ok := s.unversionedTypes[t]; ok { + kind = unversionedKind + } else { + kinds, ok := s.typeToGVK[t] + if !ok || len(kinds) == 0 { + return nil, fmt.Errorf("%v is not a registered type and cannot be converted into version %q", t, outGroupVersionString) + } + kind = kinds[0] + } + + outKind := outVersion.WithKind(kind.Kind) inKind, err := s.ObjectKind(in) if err != nil { @@ -355,10 +398,6 @@ func (s *Scheme) ConvertToVersion(in interface{}, outGroupVersionString string) return nil, err } - if err := s.SetVersionAndKind(outVersion.String(), outKind.Kind, out); err != nil { - return nil, err - } - return out, nil } @@ -367,6 +406,14 @@ func (s *Scheme) Converter() *Converter { return s.converter } +// WithConversions returns a scheme with additional conversion functions +func (s *Scheme) WithConversions(fns ConversionFuncs) *Scheme { + c := s.converter.WithConversions(fns) + copied := *s + copied.converter = c + return &copied +} + // generateConvertMeta constructs the meta value we pass to Convert. func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversioned.GroupVersion, in interface{}) (FieldMatchingFlags, *Meta) { t := reflect.TypeOf(in) @@ -377,12 +424,6 @@ func (s *Scheme) generateConvertMeta(srcGroupVersion, destGroupVersion unversion } } -// DataKind will return the group,version,kind of the given wire-format -// encoding of an API Object, or an error. -func (s *Scheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { - return s.MetaFactory.Interpret(data) -} - // ObjectKind returns the group,version,kind of the go object, // or an error if it's not a pointer or is unregistered. func (s *Scheme) ObjectKind(obj interface{}) (unversioned.GroupVersionKind, error) { @@ -390,7 +431,6 @@ func (s *Scheme) ObjectKind(obj interface{}) (unversioned.GroupVersionKind, erro if err != nil { return unversioned.GroupVersionKind{}, err } - return gvks[0], nil } @@ -399,22 +439,16 @@ func (s *Scheme) ObjectKind(obj interface{}) (unversioned.GroupVersionKind, erro func (s *Scheme) ObjectKinds(obj interface{}) ([]unversioned.GroupVersionKind, error) { v, err := EnforcePtr(obj) if err != nil { - return []unversioned.GroupVersionKind{}, err + return nil, err } t := v.Type() gvks, ok := s.typeToGVK[t] if !ok { - return []unversioned.GroupVersionKind{}, ¬RegisteredErr{t: t} + return nil, ¬RegisteredErr{t: t} } - return gvks, nil -} -// SetVersionAndKind sets the version and kind fields (with help from -// MetaInsertionFactory). Returns an error if this isn't possible. obj -// must be a pointer. -func (s *Scheme) SetVersionAndKind(version, kind string, obj interface{}) error { - return s.MetaFactory.Update(version, kind, obj) + return gvks, nil } // maybeCopy copies obj if it is not a pointer, to get a settable/addressable diff --git a/pkg/conversion/scheme_test.go b/pkg/conversion/scheme_test.go index 9c62f407d28..766aa8b479b 100644 --- a/pkg/conversion/scheme_test.go +++ b/pkg/conversion/scheme_test.go @@ -18,15 +18,11 @@ package conversion import ( "encoding/json" - "fmt" - "reflect" - "strings" "testing" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/util" - "github.com/ghodss/yaml" "github.com/google/gofuzz" flag "github.com/spf13/pflag" ) @@ -109,7 +105,7 @@ var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs( // Returns a new Scheme set up with the test objects. func GetTestScheme() *Scheme { - internalGV := unversioned.GroupVersion{} + internalGV := unversioned.GroupVersion{Version: "__internal"} externalGV := unversioned.GroupVersion{Version: "v1"} s := NewScheme() @@ -122,34 +118,9 @@ func GetTestScheme() *Scheme { s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{}) s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{}) s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{}) - s.MetaFactory = testMetaFactory{} return s } -type testMetaFactory struct{} - -func (testMetaFactory) Interpret(data []byte) (unversioned.GroupVersionKind, error) { - findKind := struct { - APIVersion string `json:"myVersionKey,omitempty"` - ObjectKind string `json:"myKindKey,omitempty"` - }{} - // yaml is a superset of json, so we use it to decode here. That way, - // we understand both. - err := yaml.Unmarshal(data, &findKind) - if err != nil { - return unversioned.GroupVersionKind{}, fmt.Errorf("couldn't get version/kind: %v", err) - } - gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) - if err != nil { - return unversioned.GroupVersionKind{}, err - } - return gv.WithKind(findKind.ObjectKind), nil -} - -func (testMetaFactory) Update(version, kind string, obj interface{}) error { - return UpdateVersionAndKind(nil, "APIVersion", version, "ObjectKind", kind, obj) -} - func objDiff(a, b interface{}) string { ab, err := json.Marshal(a) if err != nil { @@ -170,111 +141,6 @@ func objDiff(a, b interface{}) string { //) } -func runTest(t *testing.T, source interface{}) { - name := reflect.TypeOf(source).Elem().Name() - TestObjectFuzzer.Fuzz(source) - - s := GetTestScheme() - data, err := s.EncodeToVersion(source, "v1") - if err != nil { - t.Errorf("%v: %v (%#v)", name, err, source) - return - } - obj2, err := s.Decode(data) - if err != nil { - t.Errorf("%v: %v (%v)", name, err, string(data)) - return - } - if !reflect.DeepEqual(source, obj2) { - t.Errorf("1: %v: diff: %v", name, objDiff(source, obj2)) - return - } - obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface() - err = s.DecodeInto(data, obj3) - if err != nil { - t.Errorf("2: %v: %v", name, err) - return - } - if !reflect.DeepEqual(source, obj3) { - t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3)) - return - } -} - -func TestTypes(t *testing.T) { - table := []interface{}{ - &TestType1{}, - &ExternalInternalSame{}, - } - for _, item := range table { - // Try a few times, since runTest uses random values. - for i := 0; i < *fuzzIters; i++ { - runTest(t, item) - } - } -} - -func TestMultipleNames(t *testing.T) { - s := GetTestScheme() - - obj, err := s.Decode([]byte(`{"myKindKey":"TestType3","myVersionKey":"v1","A":"value"}`)) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - internal := obj.(*TestType1) - if internal.A != "value" { - t.Fatalf("unexpected decoded object: %#v", internal) - } - - out, err := s.EncodeToVersion(internal, "v1") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(string(out), `"myKindKey":"TestType1"`) { - t.Errorf("unexpected encoded output: %s", string(out)) - } -} - -func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) { - internalGV := unversioned.GroupVersion{} - externalGV := unversioned.GroupVersion{Version: "v1"} - - s := NewScheme() - // create two names internally, with TestType1 being preferred - s.AddKnownTypeWithName(internalGV.WithKind("TestType1"), &TestType1{}) - s.AddKnownTypeWithName(internalGV.WithKind("OtherType1"), &TestType1{}) - // create two names externally, with TestType1 being preferred - s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) - s.AddKnownTypeWithName(externalGV.WithKind("OtherType1"), &ExternalTestType1{}) - s.MetaFactory = testMetaFactory{} - - ext := &ExternalTestType1{} - ext.APIVersion = "v1" - ext.ObjectKind = "OtherType1" - ext.A = "test" - data, err := json.Marshal(ext) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - expect := &TestType1{A: "test"} - - obj, err := s.Decode(data) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(expect, obj) { - t.Errorf("unexpected object: %#v", obj) - } - - into := &TestType1{} - if err := s.DecodeInto(data, into); err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(expect, obj) { - t.Errorf("unexpected object: %#v", obj) - } -} - func TestKnownTypes(t *testing.T) { s := GetTestScheme() if len(s.KnownTypes(unversioned.GroupVersion{Group: "group", Version: "v2"})) != 0 { @@ -313,75 +179,3 @@ func TestConvertToVersionErr(t *testing.T) { t.Fatalf("unexpected non-error") } } - -func TestEncode_NonPtr(t *testing.T) { - s := GetTestScheme() - tt := TestType1{A: "I'm not a pointer object"} - obj := interface{}(tt) - data, err := s.EncodeToVersion(obj, "v1") - obj2, err2 := s.Decode(data) - if err != nil || err2 != nil { - t.Fatalf("Failure: '%v' '%v'", err, err2) - } - if _, ok := obj2.(*TestType1); !ok { - t.Fatalf("Got wrong type") - } - if !reflect.DeepEqual(obj2, &tt) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", &tt, obj2) - } -} - -func TestEncode_Ptr(t *testing.T) { - s := GetTestScheme() - tt := &TestType1{A: "I am a pointer object"} - obj := interface{}(tt) - data, err := s.EncodeToVersion(obj, "v1") - obj2, err2 := s.Decode(data) - if err != nil || err2 != nil { - t.Fatalf("Failure: '%v' '%v'", err, err2) - } - if _, ok := obj2.(*TestType1); !ok { - t.Fatalf("Got wrong type") - } - if !reflect.DeepEqual(obj2, tt) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", &tt, obj2) - } -} - -func TestBadJSONRejection(t *testing.T) { - s := GetTestScheme() - badJSONs := [][]byte{ - []byte(`{"myVersionKey":"v1"}`), // Missing kind - []byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind - []byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version - } - for _, b := range badJSONs { - if _, err := s.Decode(b); err == nil { - t.Errorf("Did not reject bad json: %s", string(b)) - } - } - badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`) - if err := s.DecodeInto(badJSONKindMismatch, &TestType1{}); err == nil { - t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) - } - if err := s.DecodeInto([]byte(``), &TestType1{}); err == nil { - t.Errorf("Did not give error for empty data") - } -} - -func TestBadJSONRejectionForSetInternalVersion(t *testing.T) { - s := GetTestScheme() - s.InternalVersions[""] = unversioned.GroupVersion{Version: "v1"} - badJSONs := [][]byte{ - []byte(`{"myKindKey":"TestType1"}`), // Missing version - } - for _, b := range badJSONs { - if _, err := s.Decode(b); err == nil { - t.Errorf("Did not reject bad json: %s", string(b)) - } - } - badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`) - if err := s.DecodeInto(badJSONKindMismatch, &TestType1{}); err == nil { - t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) - } -} diff --git a/pkg/conversion/unversioned_test.go b/pkg/conversion/unversioned_test.go index e4b0c1ea73c..5a798be95eb 100644 --- a/pkg/conversion/unversioned_test.go +++ b/pkg/conversion/unversioned_test.go @@ -26,7 +26,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" + "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/runtime" ) @@ -67,8 +67,8 @@ func TestV1EncodeDecodeStatus(t *testing.T) { func TestExperimentalEncodeDecodeStatus(t *testing.T) { // TODO: caesarxuchao: use the testapi.Extensions.Codec() once the PR that // moves experimental from v1 to v1beta1 got merged. - expCodec := runtime.CodecFor(api.Scheme, v1beta1.SchemeGroupVersion) - encoded, err := expCodec.Encode(status) + expCodec := api.Codecs.LegacyCodec(extensions.SchemeGroupVersion) + encoded, err := runtime.Encode(expCodec, status) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -79,7 +79,7 @@ func TestExperimentalEncodeDecodeStatus(t *testing.T) { if typeMeta.Kind != "Status" { t.Errorf("Kind is not set to \"Status\". Got %s", encoded) } - if typeMeta.APIVersion != "" { + if typeMeta.APIVersion != "v1" { t.Errorf("APIVersion is not set to \"\". Got %s", encoded) } decoded, err := runtime.Decode(expCodec, encoded) diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index c538d557b28..538094b2153 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/registry/generic" genericetcd "k8s.io/kubernetes/pkg/registry/generic/etcd" ipallocator "k8s.io/kubernetes/pkg/registry/service/ipallocator" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/ui" "k8s.io/kubernetes/pkg/util" @@ -148,6 +149,15 @@ type APIGroupInfo struct { // If nil, defaults to groupMeta.GroupVersion. // TODO: Remove this when https://github.com/kubernetes/kubernetes/issues/19018 is fixed. OptionsExternalVersion *unversioned.GroupVersion + + // Scheme includes all of the types used by this group and how to convert between them (or + // to convert objects from outside of this group that are accepted in this API). + // TODO: replace with interfaces + Scheme *runtime.Scheme + // NegotiatedSerializer controls how this group encodes and decodes data + NegotiatedSerializer runtime.NegotiatedSerializer + // ParameterCodec performs conversions for query parameters passed to API calls + ParameterCodec runtime.ParameterCodec } // Config is a structure used to configure a GenericAPIServer. @@ -179,6 +189,9 @@ type Config struct { // Map requests to contexts. Exported so downstream consumers can provider their own mappers RequestContextMapper api.RequestContextMapper + // Required, the interface for serializing and converting objects to and from the wire + Serializer runtime.NegotiatedSerializer + // If specified, all web services will be registered into this container RestfulContainer *restful.Container @@ -275,6 +288,10 @@ type GenericAPIServer struct { // storage contains the RESTful endpoints exposed by this GenericAPIServer storage map[string]rest.Storage + // Serializer controls how common API objects not in a group/version prefix are serialized for this server. + // Individual APIGroups may define their own serializers. + Serializer runtime.NegotiatedSerializer + // "Outputs" Handler http.Handler InsecureHandler http.Handler @@ -381,6 +398,7 @@ func New(c *Config) *GenericAPIServer { AdmissionControl: c.AdmissionControl, ApiGroupVersionOverrides: c.APIGroupVersionOverrides, RequestContextMapper: c.RequestContextMapper, + Serializer: c.Serializer, cacheTimeout: c.CacheTimeout, MinRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second, @@ -405,7 +423,7 @@ func New(c *Config) *GenericAPIServer { } else { mux := http.NewServeMux() s.mux = mux - handlerContainer = NewHandlerContainer(mux) + handlerContainer = NewHandlerContainer(mux, c.Serializer) } s.HandlerContainer = handlerContainer // Use CurlyRouter to be able to use regular expressions in paths. Regular expressions are required in paths for example for proxy (where the path is proxy/{kind}/{name}/{*}) @@ -444,10 +462,10 @@ func (s *GenericAPIServer) HandleFuncWithAuth(pattern string, handler func(http. s.MuxHelper.HandleFunc(pattern, handler) } -func NewHandlerContainer(mux *http.ServeMux) *restful.Container { +func NewHandlerContainer(mux *http.ServeMux, s runtime.NegotiatedSerializer) *restful.Container { container := restful.NewContainer() container.ServeMux = mux - apiserver.InstallRecoverHandler(container) + apiserver.InstallRecoverHandler(s, container) return container } @@ -654,7 +672,7 @@ func (s *GenericAPIServer) installAPIGroup(apiGroupInfo *APIGroupInfo) error { // Install the version handler. if apiGroupInfo.IsLegacyGroup { // Add a handler at /api to enumerate the supported api versions. - apiserver.AddApiWebService(s.HandlerContainer, apiPrefix, apiVersions) + apiserver.AddApiWebService(s.Serializer, s.HandlerContainer, apiPrefix, apiVersions) } else { // Add a handler at /apis/ to enumerate all versions supported by this group. apiVersionsForDiscovery := []unversioned.GroupVersionForDiscovery{} @@ -673,9 +691,9 @@ func (s *GenericAPIServer) installAPIGroup(apiGroupInfo *APIGroupInfo) error { Versions: apiVersionsForDiscovery, PreferredVersion: preferedVersionForDiscovery, } - apiserver.AddGroupWebService(s.HandlerContainer, apiPrefix+"/"+apiGroup.Name, apiGroup) + apiserver.AddGroupWebService(s.Serializer, s.HandlerContainer, apiPrefix+"/"+apiGroup.Name, apiGroup) } - apiserver.InstallServiceErrorHandler(s.HandlerContainer, s.NewRequestInfoResolver(), apiVersions) + apiserver.InstallServiceErrorHandler(s.Serializer, s.HandlerContainer, s.NewRequestInfoResolver(), apiVersions) return nil } @@ -687,25 +705,21 @@ func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV version, err := s.newAPIGroupVersion(apiGroupInfo.GroupMeta, groupVersion) version.Root = apiPrefix version.Storage = storage + version.ParameterCodec = apiGroupInfo.ParameterCodec + version.Serializer = apiGroupInfo.NegotiatedSerializer + version.Creater = apiGroupInfo.Scheme + version.Convertor = apiGroupInfo.Scheme + version.Typer = apiGroupInfo.Scheme return version, err } func (s *GenericAPIServer) newAPIGroupVersion(groupMeta apimachinery.GroupMeta, groupVersion unversioned.GroupVersion) (*apiserver.APIGroupVersion, error) { - versionInterface, err := groupMeta.InterfacesFor(groupVersion) - if err != nil { - return nil, err - } return &apiserver.APIGroupVersion{ RequestInfoResolver: s.NewRequestInfoResolver(), - Creater: api.Scheme, - Convertor: api.Scheme, - Typer: api.Scheme, - GroupVersion: groupVersion, Linker: groupMeta.SelfLinker, Mapper: groupMeta.RESTMapper, - Codec: versionInterface.Codec, Admit: s.AdmissionControl, Context: s.RequestContextMapper, diff --git a/pkg/genericapiserver/genericapiserver_test.go b/pkg/genericapiserver/genericapiserver_test.go index 671f6cccce4..be22c99e342 100644 --- a/pkg/genericapiserver/genericapiserver_test.go +++ b/pkg/genericapiserver/genericapiserver_test.go @@ -94,6 +94,7 @@ func TestInstallAPIGroups(t *testing.T) { config.ProxyTLSClientConfig = &tls.Config{} config.APIPrefix = "/apiPrefix" config.APIGroupPrefix = "/apiGroupPrefix" + config.Serializer = api.Codecs s := New(&config) apiGroupMeta := registered.GroupOrDie(api.GroupName) @@ -104,12 +105,16 @@ func TestInstallAPIGroups(t *testing.T) { GroupMeta: *apiGroupMeta, VersionedResourcesStorageMap: map[string]map[string]rest.Storage{}, IsLegacyGroup: true, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, }, { // extensions group version GroupMeta: *extensionsGroupMeta, VersionedResourcesStorageMap: map[string]map[string]rest.Storage{}, OptionsExternalVersion: &apiGroupMeta.GroupVersion, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, }, } s.InstallAPIGroups(apiGroupsInfo) @@ -139,7 +144,7 @@ func TestInstallAPIGroups(t *testing.T) { func TestNewHandlerContainer(t *testing.T) { assert := assert.New(t) mux := http.NewServeMux() - container := NewHandlerContainer(mux) + container := NewHandlerContainer(mux, nil) assert.Equal(mux, container.ServeMux, "ServerMux's do not match") } @@ -178,7 +183,7 @@ func TestInstallSwaggerAPI(t *testing.T) { defer etcdserver.Terminate(t) mux := http.NewServeMux() - server.HandlerContainer = NewHandlerContainer(mux) + server.HandlerContainer = NewHandlerContainer(mux, nil) // Ensure swagger isn't installed without the call ws := server.HandlerContainer.RegisteredWebServices() @@ -197,7 +202,7 @@ func TestInstallSwaggerAPI(t *testing.T) { // Empty externalHost verification mux = http.NewServeMux() - server.HandlerContainer = NewHandlerContainer(mux) + server.HandlerContainer = NewHandlerContainer(mux, nil) server.externalHost = "" server.ClusterIP = net.IPv4(10, 10, 10, 10) server.PublicReadWritePort = 1010 diff --git a/pkg/kubectl/apply.go b/pkg/kubectl/apply.go index 9edbd6fa737..3149bcb29da 100644 --- a/pkg/kubectl/apply.go +++ b/pkg/kubectl/apply.go @@ -21,6 +21,7 @@ import ( "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" ) type debugError interface { @@ -80,7 +81,7 @@ func SetOriginalConfiguration(info *resource.Info, original []byte) error { // If annotate is true, it embeds the result as an anotation in the modified // configuration. If an object was read from the command input, it will use that // version of the object. Otherwise, it will use the version from the server. -func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error) { +func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.Encoder) ([]byte, error) { // First serialize the object without the annotation to prevent recursion, // then add that serialization to it as the annotation and serialize it again. var modified []byte @@ -100,6 +101,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error original := annotations[LastAppliedConfigAnnotation] delete(annotations, LastAppliedConfigAnnotation) accessor.SetAnnotations(annotations) + // TODO: this needs to be abstracted - there should be no assumption that versioned object + // can be marshalled to JSON. modified, err = json.Marshal(info.VersionedObject) if err != nil { return nil, err @@ -108,6 +111,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error if annotate { annotations[LastAppliedConfigAnnotation] = string(modified) accessor.SetAnnotations(annotations) + // TODO: this needs to be abstracted - there should be no assumption that versioned object + // can be marshalled to JSON. modified, err = json.Marshal(info.VersionedObject) if err != nil { return nil, err @@ -136,7 +141,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error return nil, err } - modified, err = info.Mapping.Codec.Encode(info.Object) + modified, err = runtime.Encode(codec, info.Object) if err != nil { return nil, err } @@ -147,7 +152,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error return nil, err } - modified, err = info.Mapping.Codec.Encode(info.Object) + modified, err = runtime.Encode(codec, info.Object) if err != nil { return nil, err } @@ -165,17 +170,17 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error // UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied // configuration annotation is already present. Otherwise, it does nothing. -func UpdateApplyAnnotation(info *resource.Info) error { +func UpdateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error { if original, err := GetOriginalConfiguration(info); err != nil || len(original) <= 0 { return err } - return CreateApplyAnnotation(info) + return CreateApplyAnnotation(info, codec) } // CreateApplyAnnotation gets the modified configuration of the object, // without embedding it again, and then sets it on the object as the annotation. -func CreateApplyAnnotation(info *resource.Info) error { - modified, err := GetModifiedConfiguration(info, false) +func CreateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error { + modified, err := GetModifiedConfiguration(info, false, codec) if err != nil { return err } @@ -184,9 +189,9 @@ func CreateApplyAnnotation(info *resource.Info) error { // Create the annotation used by kubectl apply only when createAnnotation is true // Otherwise, only update the annotation when it already exists -func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info) error { +func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info, codec runtime.Encoder) error { if createAnnotation { - return CreateApplyAnnotation(info) + return CreateApplyAnnotation(info, codec) } - return UpdateApplyAnnotation(info) + return UpdateApplyAnnotation(info, codec) } diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index 3f82db4ff52..95869a3d534 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -151,7 +151,7 @@ func (o *AnnotateOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra } mapper, typer := f.Object() - o.builder = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + o.builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, o.filenames...). @@ -211,7 +211,7 @@ func (o AnnotateOptions) RunAnnotate() error { } mapping := info.ResourceMapping() - client, err := o.f.RESTClient(mapping) + client, err := o.f.ClientForMapping(mapping) if err != nil { return err } diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index a13165fc823..ef63b040092 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/strategicpatch" ) @@ -92,7 +93,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -104,6 +105,8 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap return err } + encoder := f.JSONEncoder() + count := 0 err = r.Visit(func(info *resource.Info, err error) error { // In this method, info.Object contains the object retrieved from the server @@ -115,7 +118,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap // Get the modified configuration of the object. Embed the result // as an annotation in the modified configuration, so that it will appear // in the patch sent to the server. - modified, err := kubectl.GetModifiedConfiguration(info, true) + modified, err := kubectl.GetModifiedConfiguration(info, true, encoder) if err != nil { return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err) } @@ -126,7 +129,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap } // Create the resource if it doesn't exist // First, update the annotation used by kubectl apply - if err := kubectl.CreateApplyAnnotation(info); err != nil { + if err := kubectl.CreateApplyAnnotation(info, encoder); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } // Then create the resource and skip the three-way merge @@ -139,7 +142,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap } // Serialize the current configuration of the object from the server. - current, err := info.Mapping.Codec.Encode(info.Object) + current, err := runtime.Encode(encoder, info.Object) if err != nil { return cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", info), info.Source, err) } diff --git a/pkg/kubectl/cmd/autoscale.go b/pkg/kubectl/cmd/autoscale.go index b8a14827faa..6647db01180 100644 --- a/pkg/kubectl/cmd/autoscale.go +++ b/pkg/kubectl/cmd/autoscale.go @@ -79,7 +79,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, filenames...). @@ -129,7 +129,12 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return err } - resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} + resourceMapper := &resource.Mapper{ + ObjectTyper: typer, + RESTMapper: mapper, + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + Decoder: f.Decoder(true), + } hpa, err := resourceMapper.InfoForObject(object) if err != nil { return err @@ -139,7 +144,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return f.PrintObject(cmd, object, out) } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa, f.JSONEncoder()); err != nil { return err } diff --git a/pkg/kubectl/cmd/clusterinfo.go b/pkg/kubectl/cmd/clusterinfo.go index a5adb2d33da..c76e28f7c63 100644 --- a/pkg/kubectl/cmd/clusterinfo.go +++ b/pkg/kubectl/cmd/clusterinfo.go @@ -45,25 +45,25 @@ func NewCmdClusterInfo(f *cmdutil.Factory, out io.Writer) *cobra.Command { return cmd } -func RunClusterInfo(factory *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { +func RunClusterInfo(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { if len(os.Args) > 1 && os.Args[1] == "clusterinfo" { printDeprecationWarning("cluster-info", "clusterinfo") } - client, err := factory.ClientConfig() + client, err := f.ClientConfig() if err != nil { return err } printService(out, "Kubernetes master", client.Host) - mapper, typer := factory.Object() + mapper, typer := f.Object() cmdNamespace := cmdutil.GetFlagString(cmd, "namespace") if cmdNamespace == "" { cmdNamespace = api.NamespaceSystem } // TODO use generalized labels once they are implemented (#341) - b := resource.NewBuilder(mapper, typer, factory.ClientMapperForCommand()). + b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). SelectorParam("kubernetes.io/cluster-service=true"). ResourceTypeOrNameArgs(false, []string{"services"}...). diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 2fff13e9b91..4db40989c67 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -38,6 +38,7 @@ import ( cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" "k8s.io/kubernetes/pkg/util" ) @@ -100,22 +101,21 @@ func versionErrIfFalse(b bool) error { } var validVersion = testapi.Default.GroupVersion().Version -var internalGV = unversioned.GroupVersion{Group: "apitest", Version: ""} +var internalGV = unversioned.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal} var unlikelyGV = unversioned.GroupVersion{Group: "apitest", Version: "unlikelyversion"} var validVersionGV = unversioned.GroupVersion{Group: "apitest", Version: validVersion} func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) { scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Type"), &internalType{}) scheme.AddKnownTypeWithName(unlikelyGV.WithKind("Type"), &externalType{}) //This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name. scheme.AddKnownTypeWithName(validVersionGV.WithKind("Type"), &ExternalType2{}) - codec := runtime.CodecFor(scheme, unlikelyGV) + codecs := serializer.NewCodecFactory(scheme) + codec := codecs.LegacyCodec(unlikelyGV) mapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{unlikelyGV, validVersionGV}, func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { return &meta.VersionInterfaces{ - Codec: runtime.CodecFor(scheme, version), ObjectConvertor: scheme, MetadataAccessor: meta.NewAccessor(), }, versionErrIfFalse(version == validVersionGV || version == unlikelyGV) @@ -183,9 +183,15 @@ func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { Object: func() (meta.RESTMapper, runtime.ObjectTyper) { return t.Mapper, t.Typer }, - RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) { + ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) { return t.Client, t.Err }, + Decoder: func(bool) runtime.Decoder { + return codec + }, + JSONEncoder: func() runtime.Encoder { + return codec + }, Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { return t.Describer, t.Err }, @@ -209,7 +215,7 @@ func NewMixedFactory(apiClient resource.RESTClient) (*cmdutil.Factory, *testFact f.Object = func() (meta.RESTMapper, runtime.ObjectTyper) { return meta.MultiRESTMapper{t.Mapper, testapi.Default.RESTMapper()}, runtime.MultiObjectTyper{t.Typer, api.Scheme} } - f.RESTClient = func(m *meta.RESTMapping) (resource.RESTClient, error) { + f.ClientForMapping = func(m *meta.RESTMapping) (resource.RESTClient, error) { if m.ObjectConvertor == api.Scheme { return apiClient, t.Err } @@ -235,9 +241,15 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { c.ExtensionsClient.Client = fakeClient.Client return c, t.Err }, - RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) { + ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) { return t.Client, t.Err }, + Decoder: func(bool) runtime.Decoder { + return testapi.Default.Codec() + }, + JSONEncoder: func() runtime.Encoder { + return testapi.Default.Codec() + }, Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { return t.Describer, t.Err }, @@ -303,7 +315,7 @@ func stringBody(body string) io.ReadCloser { // mapping := &meta.RESTMapping{ // APIVersion: version, // } -// c, err := f.RESTClient(mapping) +// c, err := f.ClientForMapping(mapping) // if err != nil { // t.Errorf("unexpected error: %v", err) // } diff --git a/pkg/kubectl/cmd/convert.go b/pkg/kubectl/cmd/convert.go index 694a005cd95..e7e04d5b99d 100644 --- a/pkg/kubectl/cmd/convert.go +++ b/pkg/kubectl/cmd/convert.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" "github.com/spf13/cobra" ) @@ -87,6 +88,7 @@ type ConvertOptions struct { filenames []string local bool + encoder runtime.Encoder out io.Writer printer kubectl.ResourcePrinter @@ -105,11 +107,12 @@ func (o *ConvertOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra. // build the builder mapper, typer := f.Object() + clientMapper := resource.ClientMapperFunc(f.ClientForMapping) if o.local { fmt.Fprintln(out, "running in local mode...") - o.builder = resource.NewBuilder(mapper, typer, f.NilClientMapperForCommand()) + o.builder = resource.NewBuilder(mapper, typer, resource.DisabledClientForMapping{clientMapper}, f.Decoder(true)) } else { - o.builder = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()) + o.builder = resource.NewBuilder(mapper, typer, clientMapper, f.Decoder(true)) schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) if err != nil { return err @@ -136,6 +139,7 @@ func (o *ConvertOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra. outputFormat = "template" } } + o.encoder = f.JSONEncoder() o.printer, _, err = kubectl.GetPrinter(outputFormat, templateFile) if err != nil { return err @@ -151,7 +155,7 @@ func (o *ConvertOptions) RunConvert() error { return err } - objects, err := resource.AsVersionedObject(infos, false, o.outputVersion.String()) + objects, err := resource.AsVersionedObject(infos, false, o.outputVersion.String(), o.encoder) if err != nil { return err } diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 6b887a07627..10b30ba4ef3 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -99,7 +99,7 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *C } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -116,7 +116,7 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *C if err != nil { return err } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } @@ -218,16 +218,20 @@ func RunCreateSubcommand(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, if err != nil { return err } - client, err := f.RESTClient(mapping) + client, err := f.ClientForMapping(mapping) if err != nil { return err } - resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} + resourceMapper := &resource.Mapper{ + ObjectTyper: typer, + RESTMapper: mapper, + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + } info, err := resourceMapper.InfoForObject(obj) if err != nil { return err } - if err := kubectl.UpdateApplyAnnotation(info); err != nil { + if err := kubectl.UpdateApplyAnnotation(info, f.JSONEncoder()); err != nil { return err } if !options.DryRun { diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 6bac41cdec6..94973d6e584 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -108,7 +108,7 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } deleteAll := cmdutil.GetFlagBool(cmd, "all") mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 7fa4dedf196..3ea83ecdc4f 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -106,7 +106,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -147,7 +147,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s } func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f *cmdutil.Factory, namespace, rsrc, prefix string, out io.Writer, originalError error) error { - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(namespace).DefaultNamespace(). ResourceTypeOrNameArgs(true, rsrc). SingleResourceType(). diff --git a/pkg/kubectl/cmd/drain.go b/pkg/kubectl/cmd/drain.go index db09620e1e1..8c07a18281a 100644 --- a/pkg/kubectl/cmd/drain.go +++ b/pkg/kubectl/cmd/drain.go @@ -209,8 +209,7 @@ func (o *DrainOptions) getPodsForDeletion() ([]api.Pod, error) { if found { // Now verify that the specified creator actually exists. var sr api.SerializedReference - err := api.Scheme.DecodeInto([]byte(creatorRef), &sr) - if err != nil { + if err := runtime.DecodeInto(o.factory.Decoder(true), []byte(creatorRef), &sr); err != nil { return pods, err } if sr.Reference.Kind == "ReplicationController" { diff --git a/pkg/kubectl/cmd/drain_test.go b/pkg/kubectl/cmd/drain_test.go index e4daed8ae91..10d2ef268fc 100644 --- a/pkg/kubectl/cmd/drain_test.go +++ b/pkg/kubectl/cmd/drain_test.go @@ -441,7 +441,7 @@ func refJson(t *testing.T, o runtime.Object) string { } _, _, codec := NewAPIFactory() - json, err := codec.Encode(&api.SerializedReference{Reference: *ref}) + json, err := runtime.Encode(codec, &api.SerializedReference{Reference: *ref}) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/pkg/kubectl/cmd/edit.go b/pkg/kubectl/cmd/edit.go index 35036206043..e98407bf846 100644 --- a/pkg/kubectl/cmd/edit.go +++ b/pkg/kubectl/cmd/edit.go @@ -23,7 +23,7 @@ import ( "fmt" "io" "os" - "runtime" + gruntime "runtime" "strings" "k8s.io/kubernetes/pkg/api" @@ -34,6 +34,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor" "k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/strategicpatch" "k8s.io/kubernetes/pkg/util/yaml" @@ -94,7 +95,7 @@ func NewCmdEdit(f *cmdutil.Factory, out io.Writer) *cobra.Command { kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.") cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).") - cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)") + cmd.Flags().Bool("windows-line-endings", gruntime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)") cmdutil.AddApplyAnnotationFlags(cmd) return cmd } @@ -119,13 +120,14 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin } mapper, typer := f.Object() - rmap := &resource.Mapper{ + resourceMapper := &resource.Mapper{ ObjectTyper: typer, RESTMapper: mapper, - ClientMapper: f.ClientMapperForCommand(), + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + Decoder: f.Decoder(true), } - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, filenames...). ResourceTypeOrNameArgs(true, args...). @@ -147,6 +149,8 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin return err } + encoder := f.JSONEncoder() + windowsLineEndings := cmdutil.GetFlagBool(cmd, "windows-line-endings") edit := editor.NewDefaultEditor(f.EditorEnvs()) defaultVersion, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion) @@ -155,7 +159,7 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin } results := editResults{} for { - objs, err := resource.AsVersionedObjects(infos, defaultVersion.String()) + objs, err := resource.AsVersionedObjects(infos, defaultVersion.String(), encoder) if err != nil { return preservedFile(err, results.file, out) } @@ -219,21 +223,21 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin } // parse the edited file - updates, err := rmap.InfoForData(edited, "edited-file") + updates, err := resourceMapper.InfoForData(edited, "edited-file") if err != nil { return fmt.Errorf("The edited file had a syntax error: %v", err) } // put configuration annotation in "updates" - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), updates); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), updates, encoder); err != nil { return preservedFile(err, file, out) } // encode updates back to "edited" since we'll only generate patch from "edited" - if edited, err = updates.Mapping.Codec.Encode(updates.Object); err != nil { + if edited, err = runtime.Encode(encoder, updates.Object); err != nil { return preservedFile(err, file, out) } - visitor := resource.NewFlattenListVisitor(updates, rmap) + visitor := resource.NewFlattenListVisitor(updates, resourceMapper) // need to make sure the original namespace wasn't changed while editing if err = visitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil { diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index 382a6c098e4..05469713164 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/validation" ) @@ -104,7 +105,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -192,13 +193,19 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str } if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 { - object, err = cmdutil.Merge(object, inline, mapping.GroupVersionKind.Kind) + codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true)) + object, err = cmdutil.Merge(codec, object, inline, mapping.GroupVersionKind.Kind) if err != nil { return err } } - resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} + resourceMapper := &resource.Mapper{ + ObjectTyper: typer, + RESTMapper: mapper, + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + Decoder: f.Decoder(true), + } info, err = resourceMapper.InfoForObject(object) if err != nil { return err @@ -207,7 +214,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str if cmdutil.GetFlagBool(cmd, "dry-run") { return f.PrintObject(cmd, object, out) } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return err } diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index e4c3f009b2e..284e3555052 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -137,7 +137,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string // handle watch separately since we cannot watch multiple resource types isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only") if isWatch || isWatchOnly { - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(selector). @@ -192,7 +192,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string return nil } - b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). FilenameParam(enforceNamespace, options.Filenames...). SelectorParam(selector). @@ -224,7 +224,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string if err != nil { return err } - obj, err := resource.AsVersionedObject(infos, !singular, version.String()) + obj, err := resource.AsVersionedObject(infos, !singular, version.String(), f.JSONEncoder()) if err != nil { return err } @@ -244,7 +244,8 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string sorting, err := cmd.Flags().GetString("sort-by") var sorter *kubectl.RuntimeSort if err == nil && len(sorting) > 0 { - if sorter, err = kubectl.SortObjects(objs, sorting); err != nil { + // TODO: questionable + if sorter, err = kubectl.SortObjects(f.Decoder(true), objs, sorting); err != nil { return err } } diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index 7cedaf51786..d8ba37e2b91 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -177,7 +177,7 @@ func TestGetUnknownSchemaObjectListGeneric(t *testing.T) { }, } for k, test := range testCases { - apiCodec := runtime.CodecFor(api.Scheme, *testapi.Default.GroupVersion()) + apiCodec := testapi.Default.Codec() regularClient := &fake.RESTClient{ Codec: apiCodec, Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { @@ -482,7 +482,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if errs := runtime.DecodeList(list, api.Scheme); len(errs) > 0 { + if errs := runtime.DecodeList(list, codec); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := meta.SetList(out, list); err != nil { diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index d2952d201fa..23f7b605644 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -201,7 +201,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri return cmdutil.UsageError(cmd, err.Error()) } mapper, typer := f.Object() - b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -264,7 +264,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri } mapping := info.ResourceMapping() - client, err := f.RESTClient(mapping) + client, err := f.ClientForMapping(mapping) if err != nil { return err } diff --git a/pkg/kubectl/cmd/logs.go b/pkg/kubectl/cmd/logs.go index 5f1b47c6a52..369d41d8a96 100644 --- a/pkg/kubectl/cmd/logs.go +++ b/pkg/kubectl/cmd/logs.go @@ -59,6 +59,7 @@ type LogsOptions struct { Mapper meta.RESTMapper Typer runtime.ObjectTyper ClientMapper resource.ClientMapper + Decoder runtime.Decoder LogsForObject func(object, options runtime.Object) (*client.Request, error) @@ -151,7 +152,8 @@ func (o *LogsOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Com o.Options = logOptions o.Mapper, o.Typer = f.Object() - o.ClientMapper = f.ClientMapperForCommand() + o.Decoder = f.Decoder(true) + o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping) o.LogsForObject = f.LogsForObject o.Out = out @@ -176,7 +178,7 @@ func (o LogsOptions) Validate() error { // RunLogs retrieves a pod log func (o LogsOptions) RunLogs() (int64, error) { - infos, err := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper). + infos, err := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper, o.Decoder). NamespaceParam(o.Namespace).DefaultNamespace(). ResourceNames("pods", o.ResourceArg). SingleResourceType(). diff --git a/pkg/kubectl/cmd/patch.go b/pkg/kubectl/cmd/patch.go index 9a971d72978..b537adaa186 100644 --- a/pkg/kubectl/cmd/patch.go +++ b/pkg/kubectl/cmd/patch.go @@ -92,7 +92,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -114,7 +114,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri info := infos[0] name, namespace := info.Name, info.Namespace mapping := info.ResourceMapping() - client, err := f.RESTClient(mapping) + client, err := f.ClientForMapping(mapping) if err != nil { return err } diff --git a/pkg/kubectl/cmd/replace.go b/pkg/kubectl/cmd/replace.go index a076e91f9ab..226e5c434d0 100644 --- a/pkg/kubectl/cmd/replace.go +++ b/pkg/kubectl/cmd/replace.go @@ -112,7 +112,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -129,7 +129,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st return err } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return cmdutil.AddSourceToErr("replacing", info.Source, err) } @@ -174,7 +174,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). @@ -198,7 +198,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return err } - r = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -216,7 +216,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return err } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return err } diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index fb11b9a3cf7..bf4015b6e62 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -29,7 +29,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" @@ -198,7 +197,7 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg return err } - request := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + request := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). Schema(schema). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, filename). @@ -385,12 +384,9 @@ func isReplicasDefaulted(info *resource.Info) bool { // was unable to recover versioned info return false } - - switch info.Mapping.GroupVersionKind.GroupVersion() { - case unversioned.GroupVersion{Version: "v1"}: - if rc, ok := info.VersionedObject.(*v1.ReplicationController); ok { - return rc.Spec.Replicas == nil - } + switch t := info.VersionedObject.(type) { + case *v1.ReplicationController: + return t.Spec.Replicas == nil } return false } diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index a87efcee0d6..8affcbda870 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -236,7 +236,7 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob return err } _, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). ResourceNames(mapping.Resource, name). @@ -409,7 +409,8 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub } if len(overrides) > 0 { - obj, err = cmdutil.Merge(obj, overrides, groupVersionKind.Kind) + codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true)) + obj, err = cmdutil.Merge(codec, obj, overrides, groupVersionKind.Kind) if err != nil { return nil, "", nil, nil, err } @@ -419,20 +420,25 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub if err != nil { return nil, "", nil, nil, err } - client, err := f.RESTClient(mapping) + client, err := f.ClientForMapping(mapping) if err != nil { return nil, "", nil, nil, err } // TODO: extract this flag to a central location, when such a location exists. if !cmdutil.GetFlagBool(cmd, "dry-run") { - resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} + resourceMapper := &resource.Mapper{ + ObjectTyper: typer, + RESTMapper: mapper, + ClientMapper: resource.ClientMapperFunc(f.ClientForMapping), + Decoder: f.Decoder(true), + } info, err := resourceMapper.InfoForObject(obj) if err != nil { return nil, "", nil, nil, err } - if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil { return nil, "", nil, nil, err } diff --git a/pkg/kubectl/cmd/scale.go b/pkg/kubectl/cmd/scale.go index 18e9fedbffd..2637fa2b0af 100644 --- a/pkg/kubectl/cmd/scale.go +++ b/pkg/kubectl/cmd/scale.go @@ -105,7 +105,7 @@ func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, options.Filenames...). diff --git a/pkg/kubectl/cmd/stop.go b/pkg/kubectl/cmd/stop.go index c6673fa481e..6069866171a 100644 --- a/pkg/kubectl/cmd/stop.go +++ b/pkg/kubectl/cmd/stop.go @@ -85,7 +85,7 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Write } mapper, typer := f.Object() - r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). + r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). ResourceTypeOrNameArgs(false, args...). diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 66f9ebfab12..3d52e44eba1 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -45,6 +45,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" "k8s.io/kubernetes/pkg/util" ) @@ -63,13 +64,19 @@ type Factory struct { // Returns interfaces for dealing with arbitrary runtime.Objects. Object func() (meta.RESTMapper, runtime.ObjectTyper) + // Returns interfaces for decoding objects - if toInternal is set, decoded objects will be converted + // into their internal form (if possible). Eventually the internal form will be removed as an option, + // and only versioned objects will be returned. + Decoder func(toInternal bool) runtime.Decoder + // Returns an encoder capable of encoding a provided object into JSON in the default desired version. + JSONEncoder func() runtime.Encoder // Returns a client for accessing Kubernetes resources or an error. Client func() (*client.Client, error) // Returns a client.Config for accessing the Kubernetes server. ClientConfig func() (*client.Config, error) // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended // for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer. - RESTClient func(mapping *meta.RESTMapping) (resource.RESTClient, error) + ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error) // Returns a Describer for displaying the specified RESTMapping type or an error. Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error) // Returns a Printer for formatting objects of the given type or an error. @@ -185,7 +192,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { ClientConfig: func() (*client.Config, error) { return clients.ClientConfigForVersion(nil) }, - RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { + ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() client, err := clients.ClientForVersion(&mappingVersion) if err != nil { @@ -210,6 +217,15 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) }, + Decoder: func(toInternal bool) runtime.Decoder { + if toInternal { + return api.Codecs.UniversalDecoder() + } + return api.Codecs.UniversalDeserializer() + }, + JSONEncoder: func() runtime.Encoder { + return api.Codecs.LegacyCodec(registered.EnabledVersions()...) + }, Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, absoluteTimestamps, columnLabels), nil }, @@ -531,7 +547,7 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac } func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { - gvk, err := runtime.UnstructuredJSONScheme.DataKind(data) + gvk, err := json.DefaultMetaFactory.Interpret(data) if err != nil { return err } @@ -663,24 +679,9 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin return printer, nil } -// ClientMapperForCommand returns a ClientMapper for the factory. -func (f *Factory) ClientMapperForCommand() resource.ClientMapper { - return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return f.RESTClient(mapping) - }) -} - -// NilClientMapperForCommand returns a ClientMapper which always returns nil. -// When command is running locally and client isn't needed, this mapper can be parsed to NewBuilder. -func (f *Factory) NilClientMapperForCommand() resource.ClientMapper { - return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) { - return nil, nil - }) -} - // One stop shopping for a Builder func (f *Factory) NewBuilder() *resource.Builder { mapper, typer := f.Object() - return resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()) + return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)) } diff --git a/pkg/kubectl/cmd/util/factory_test.go b/pkg/kubectl/cmd/util/factory_test.go index 7818e7b9049..be9484fae3c 100644 --- a/pkg/kubectl/cmd/util/factory_test.go +++ b/pkg/kubectl/cmd/util/factory_test.go @@ -245,7 +245,7 @@ func TestValidateCachesSchema(t *testing.T) { os.RemoveAll(dir) obj := &api.Pod{} - data, err := testapi.Default.Codec().Encode(obj) + data, err := runtime.Encode(testapi.Default.Codec(), obj) if err != nil { t.Errorf("unexpected error: %v", err) t.FailNow() diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 67e3b01c018..72327493460 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -18,7 +18,6 @@ package util import ( "bytes" - "encoding/json" "fmt" "io" "io/ioutil" @@ -28,10 +27,8 @@ import ( "strings" "time" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" @@ -371,38 +368,11 @@ func ReadConfigDataFromLocation(location string) ([]byte, error) { } } -func Merge(dst runtime.Object, fragment, kind string) (runtime.Object, error) { - // Ok, this is a little hairy, we'd rather not force the user to specify a kind for their JSON - // So we pull it into a map, add the Kind field, and then reserialize. - // We also pull the apiVersion for proper parsing - var intermediate interface{} - if err := json.Unmarshal([]byte(fragment), &intermediate); err != nil { - return nil, err - } - dataMap, ok := intermediate.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("Expected a map, found something else: %s", fragment) - } - version, found := dataMap["apiVersion"] - if !found { - return nil, fmt.Errorf("Inline JSON requires an apiVersion field") - } - versionString, ok := version.(string) - if !ok { - return nil, fmt.Errorf("apiVersion must be a string") - } - groupVersion, err := unversioned.ParseGroupVersion(versionString) - if err != nil { - return nil, err - } - - i, err := registered.GroupOrDie(api.GroupName).InterfacesFor(groupVersion) - if err != nil { - return nil, err - } - +// Merge requires JSON serialization +// TODO: merge assumes JSON serialization, and does not properly abstract API retrieval +func Merge(codec runtime.Codec, dst runtime.Object, fragment, kind string) (runtime.Object, error) { // encode dst into versioned json and apply fragment directly too it - target, err := i.Codec.Encode(dst) + target, err := runtime.Encode(codec, dst) if err != nil { return nil, err } @@ -410,7 +380,7 @@ func Merge(dst runtime.Object, fragment, kind string) (runtime.Object, error) { if err != nil { return nil, err } - out, err := i.Codec.Decode(patched) + out, err := runtime.Decode(codec, patched) if err != nil { return nil, err } @@ -443,7 +413,7 @@ func DumpReaderToFile(reader io.Reader, filename string) error { } // UpdateObject updates resource object with updateFn -func UpdateObject(info *resource.Info, updateFn func(runtime.Object) error) (runtime.Object, error) { +func UpdateObject(info *resource.Info, codec runtime.Codec, updateFn func(runtime.Object) error) (runtime.Object, error) { helper := resource.NewHelper(info.Client, info.Mapping) if err := updateFn(info.Object); err != nil { @@ -451,7 +421,7 @@ func UpdateObject(info *resource.Info, updateFn func(runtime.Object) error) (run } // Update the annotation used by kubectl apply - if err := kubectl.UpdateApplyAnnotation(info); err != nil { + if err := kubectl.UpdateApplyAnnotation(info, codec); err != nil { return nil, err } diff --git a/pkg/kubectl/cmd/util/helpers_test.go b/pkg/kubectl/cmd/util/helpers_test.go index 3c878b4a01c..598c3e961e2 100644 --- a/pkg/kubectl/cmd/util/helpers_test.go +++ b/pkg/kubectl/cmd/util/helpers_test.go @@ -183,7 +183,7 @@ func TestMerge(t *testing.T) { } for i, test := range tests { - out, err := Merge(test.obj, test.fragment, test.kind) + out, err := Merge(testapi.Default.Codec(), test.obj, test.fragment, test.kind) if !test.expectErr { if err != nil { t.Errorf("testcase[%d], unexpected error: %v", i, err) diff --git a/pkg/kubectl/custom_column_printer.go b/pkg/kubectl/custom_column_printer.go index 7d91691f734..431a114c748 100644 --- a/pkg/kubectl/custom_column_printer.go +++ b/pkg/kubectl/custom_column_printer.go @@ -26,7 +26,6 @@ import ( "strings" "text/tabwriter" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/jsonpath" @@ -152,6 +151,7 @@ type Column struct { // of data from templates specified in the `Columns` array type CustomColumnsPrinter struct { Columns []Column + Decoder runtime.Decoder } func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error { @@ -192,7 +192,7 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso switch u := obj.(type) { case *runtime.Unknown: var err error - if obj, err = api.Codec.Decode(u.RawJSON); err != nil { + if obj, _, err = s.Decoder.Decode(u.RawJSON, nil, nil); err != nil { return err } } diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index a9dd31267e2..61b0f113184 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -80,9 +80,9 @@ type resourceTuple struct { } // NewBuilder creates a builder that operates on generic objects. -func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper) *Builder { +func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper, decoder runtime.Decoder) *Builder { return &Builder{ - mapper: &Mapper{typer, mapper, clientMapper}, + mapper: &Mapper{typer, mapper, clientMapper, decoder}, requireObject: true, } } diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index 2ee364b05c5..1fad662b63f 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -34,6 +34,7 @@ import ( "k8s.io/kubernetes/pkg/api/testapi" apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/client/unversioned/fake" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" @@ -177,9 +178,9 @@ func (v *testVisitor) Objects() []runtime.Object { return objects } -func TestPathBuilder(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). - FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml") +func TestPathBuilderAndVersionedObjectNotDefaulted(t *testing.T) { + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). + FilenameParam(false, "../../../docs/user-guide/update-demo/kitten-rc.yaml") test := &testVisitor{} singular := false @@ -190,9 +191,14 @@ func TestPathBuilder(t *testing.T) { } info := test.Infos[0] - if info.Name != "redis-master" || info.Namespace != "" || info.Object == nil { + if info.Name != "update-demo-kitten" || info.Namespace != "" || info.Object == nil { t.Errorf("unexpected info: %#v", info) } + version, ok := info.VersionedObject.(*v1.ReplicationController) + // versioned object does not have defaulting applied + if info.VersionedObject == nil || !ok || version.Spec.Replicas != nil { + t.Errorf("unexpected versioned object: %#v", info.VersionedObject) + } } func TestNodeBuilder(t *testing.T) { @@ -212,7 +218,7 @@ func TestNodeBuilder(t *testing.T) { w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), node))) }() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").Stream(r, "STDIN") test := &testVisitor{} @@ -228,7 +234,7 @@ func TestNodeBuilder(t *testing.T) { } func TestPathBuilderWithMultiple(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). FilenameParam(false, "../../../examples/pod"). NamespaceParam("test").DefaultNamespace() @@ -252,7 +258,7 @@ func TestPathBuilderWithMultiple(t *testing.T) { } func TestDirectoryBuilder(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, "../../../examples/guestbook"). NamespaceParam("test").DefaultNamespace() @@ -283,7 +289,7 @@ func TestNamespaceOverride(t *testing.T) { // TODO: Uncomment when fix #19254 // defer s.Close() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, s.URL). NamespaceParam("test") @@ -294,7 +300,7 @@ func TestNamespaceOverride(t *testing.T) { t.Fatalf("unexpected response: %v %#v", err, test.Infos) } - b = NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b = NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(true, s.URL). NamespaceParam("test") @@ -314,7 +320,7 @@ func TestURLBuilder(t *testing.T) { // TODO: Uncomment when fix #19254 // defer s.Close() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, s.URL). NamespaceParam("test") @@ -339,7 +345,7 @@ func TestURLBuilderRequireNamespace(t *testing.T) { // TODO: Uncomment when fix #19254 // defer s.Close() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). FilenameParam(false, s.URL). NamespaceParam("test").RequireNamespace() @@ -356,7 +362,7 @@ func TestResourceByName(t *testing.T) { pods, _ := testData() b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - })). + }), testapi.Default.Codec()). NamespaceParam("test") test := &testVisitor{} @@ -392,7 +398,7 @@ func TestMultipleResourceByTheSameName(t *testing.T) { "/namespaces/test/pods/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[1]), "/namespaces/test/services/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]), - })). + }), testapi.Default.Codec()). NamespaceParam("test") test := &testVisitor{} @@ -422,7 +428,7 @@ func TestResourceNames(t *testing.T) { b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]), - })). + }), testapi.Default.Codec()). NamespaceParam("test") test := &testVisitor{} @@ -446,7 +452,7 @@ func TestResourceNames(t *testing.T) { } func TestResourceByNameWithoutRequireObject(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{})). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{}), testapi.Default.Codec()). NamespaceParam("test") test := &testVisitor{} @@ -482,7 +488,7 @@ func TestResourceByNameAndEmptySelector(t *testing.T) { pods, _ := testData() b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), - })). + }), testapi.Default.Codec()). NamespaceParam("test"). SelectorParam(""). ResourceTypeOrNameArgs(true, "pods", "foo") @@ -511,7 +517,7 @@ func TestSelector(t *testing.T) { b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), "/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc), - })). + }), testapi.Default.Codec()). SelectorParam("a=b"). NamespaceParam("test"). Flatten() @@ -539,7 +545,7 @@ func TestSelector(t *testing.T) { } func TestSelectorRequiresKnownTypes(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). SelectorParam("a=b"). NamespaceParam("test"). ResourceTypes("unknown") @@ -550,7 +556,7 @@ func TestSelectorRequiresKnownTypes(t *testing.T) { } func TestSingleResourceType(t *testing.T) { - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). SelectorParam("a=b"). SingleResourceType(). ResourceTypeOrNameArgs(true, "pods,services") @@ -620,7 +626,7 @@ func TestResourceTuple(t *testing.T) { } } - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith(k, t, expectedRequests)). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith(k, t, expectedRequests), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). ResourceTypeOrNameArgs(true, testCase.args...).RequireObject(requireObject) @@ -651,7 +657,7 @@ func TestResourceTuple(t *testing.T) { func TestStream(t *testing.T) { r, pods, rc := streamTestData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").Stream(r, "STDIN").Flatten() test := &testVisitor{} @@ -668,7 +674,7 @@ func TestStream(t *testing.T) { func TestYAMLStream(t *testing.T) { r, pods, rc := streamYAMLTestData() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").Stream(r, "STDIN").Flatten() test := &testVisitor{} @@ -685,7 +691,7 @@ func TestYAMLStream(t *testing.T) { func TestMultipleObject(t *testing.T) { r, pods, svc := streamTestData() - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").Stream(r, "STDIN").Flatten(). Do().Object() @@ -707,7 +713,7 @@ func TestMultipleObject(t *testing.T) { func TestContinueOnErrorVisitor(t *testing.T) { r, _, _ := streamTestData() - req := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + req := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). ContinueOnError(). NamespaceParam("test").Stream(r, "STDIN").Flatten(). Do() @@ -736,7 +742,7 @@ func TestContinueOnErrorVisitor(t *testing.T) { } func TestSingularObject(t *testing.T) { - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). Flatten(). @@ -756,7 +762,7 @@ func TestSingularObject(t *testing.T) { } func TestSingularObjectNoExtension(t *testing.T) { - obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). FilenameParam(false, "../../../examples/pod"). Flatten(). @@ -778,7 +784,7 @@ func TestSingularObjectNoExtension(t *testing.T) { func TestSingularRootScopedObject(t *testing.T) { node := &api.Node{ObjectMeta: api.ObjectMeta{Name: "test"}, Spec: api.NodeSpec{ExternalID: "test"}} r := streamTestObject(node) - infos, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + infos, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). Stream(r, "STDIN"). Flatten(). @@ -805,7 +811,7 @@ func TestListObject(t *testing.T) { labelKey := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), - })). + }), testapi.Default.Codec()). SelectorParam("a=b"). NamespaceParam("test"). ResourceTypeOrNameArgs(true, "pods"). @@ -839,7 +845,7 @@ func TestListObjectWithDifferentVersions(t *testing.T) { obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), "/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc), - })). + }), testapi.Default.Codec()). SelectorParam("a=b"). NamespaceParam("test"). ResourceTypeOrNameArgs(true, "pods,services"). @@ -867,7 +873,7 @@ func TestWatch(t *testing.T) { Type: watch.Added, Object: &svc.Items[0], }), - })). + }), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). FilenameParam(false, "../../../examples/guestbook/redis-master-service.yaml").Flatten(). Do().Watch("12") @@ -894,7 +900,7 @@ func TestWatch(t *testing.T) { } func TestWatchMultipleError(t *testing.T) { - _, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + _, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). NamespaceParam("test").DefaultNamespace(). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). @@ -921,7 +927,7 @@ func TestLatest(t *testing.T) { "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), newPod), "/namespaces/test/pods/bar": runtime.EncodeOrDie(testapi.Default.Codec(), newPod2), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), newSvc), - })). + }), testapi.Default.Codec()). NamespaceParam("other").Stream(r, "STDIN").Flatten().Latest() test := &testVisitor{} @@ -953,7 +959,7 @@ func TestReceiveMultipleErrors(t *testing.T) { w2.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]))) }() - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()). Stream(r, "1").Stream(r2, "2"). ContinueOnError() @@ -997,7 +1003,7 @@ func TestReplaceAliases(t *testing.T) { }, } - b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()) + b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()) for _, test := range tests { replaced := b.replaceAliases(test.arg) diff --git a/pkg/kubectl/resource/helper.go b/pkg/kubectl/resource/helper.go index 85f7bc1946c..fcce5b0cae6 100644 --- a/pkg/kubectl/resource/helper.go +++ b/pkg/kubectl/resource/helper.go @@ -33,8 +33,6 @@ type Helper struct { Resource string // A RESTClient capable of mutating this resource. RESTClient RESTClient - // A codec for decoding and encoding objects of this resource type. - Codec runtime.Codec // An interface for reading or writing the resource version of this // type. Versioner runtime.ResourceVersioner @@ -45,9 +43,8 @@ type Helper struct { // NewHelper creates a Helper from a ResourceMapping func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper { return &Helper{ - RESTClient: client, Resource: mapping.Resource, - Codec: mapping.Codec, + RESTClient: client, Versioner: mapping.MetadataAccessor, NamespaceScoped: mapping.Scope.Name() == meta.RESTScopeNameNamespace, } diff --git a/pkg/kubectl/resource/helper_test.go b/pkg/kubectl/resource/helper_test.go index 2bdc977b0ff..541daf880f4 100644 --- a/pkg/kubectl/resource/helper_test.go +++ b/pkg/kubectl/resource/helper_test.go @@ -189,7 +189,6 @@ func TestHelperCreate(t *testing.T) { } modifier := &Helper{ RESTClient: client, - Codec: testapi.Default.Codec(), Versioner: testapi.Default.MetadataAccessor(), NamespaceScoped: true, } @@ -441,7 +440,6 @@ func TestHelperReplace(t *testing.T) { } modifier := &Helper{ RESTClient: client, - Codec: testapi.Default.Codec(), Versioner: testapi.Default.MetadataAccessor(), NamespaceScoped: true, } diff --git a/pkg/kubectl/resource/interfaces.go b/pkg/kubectl/resource/interfaces.go index 3d64609984a..54d20dfbfff 100644 --- a/pkg/kubectl/resource/interfaces.go +++ b/pkg/kubectl/resource/interfaces.go @@ -32,7 +32,7 @@ type RESTClient interface { Put() *client.Request } -// ClientMapper retrieves a client object for a given mapping +// ClientMapper abstracts retrieving a Client for mapped objects. type ClientMapper interface { ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) } diff --git a/pkg/kubectl/resource/mapper.go b/pkg/kubectl/resource/mapper.go index 2c61325bddc..0839ca4a8ea 100644 --- a/pkg/kubectl/resource/mapper.go +++ b/pkg/kubectl/resource/mapper.go @@ -20,69 +20,63 @@ import ( "fmt" "reflect" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/runtime" - "k8s.io/kubernetes/pkg/util/yaml" ) +// DisabledClientForMapping allows callers to avoid allowing remote calls when handling +// resources. +type DisabledClientForMapping struct { + ClientMapper +} + +func (f DisabledClientForMapping) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) { + return nil, nil +} + // Mapper is a convenience struct for holding references to the three interfaces // needed to create Info for arbitrary objects. type Mapper struct { runtime.ObjectTyper meta.RESTMapper ClientMapper + runtime.Decoder } // InfoForData creates an Info object for the given data. An error is returned // if any of the decoding or client lookup steps fail. Name and namespace will be // set into Info if the mapping's MetadataAccessor can retrieve them. func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { - json, err := yaml.ToJSON(data) + versions := &runtime.VersionedObjects{} + _, gvk, err := m.Decode(data, nil, versions) if err != nil { - return nil, fmt.Errorf("unable to parse %q: %v", source, err) - } - data = json - gvk, err := runtime.UnstructuredJSONScheme.DataKind(data) - if err != nil { - return nil, fmt.Errorf("unable to get type info from %q: %v", source, err) - } - if ok := registered.IsEnabledVersion(gvk.GroupVersion()); !ok { - return nil, fmt.Errorf("API version %q in %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), source, registered.EnabledVersions()) - } - if gvk.Kind == "" { - return nil, fmt.Errorf("kind not set in %q", source) + return nil, fmt.Errorf("unable to decode %q: %v", source, err) } mapping, err := m.RESTMapping(gvk.GroupKind(), gvk.Version) if err != nil { return nil, fmt.Errorf("unable to recognize %q: %v", source, err) } - obj, err := mapping.Codec.Decode(data) - if err != nil { - return nil, fmt.Errorf("unable to load %q: %v", source, err) - } + client, err := m.ClientForMapping(mapping) if err != nil { return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err) } + // TODO: decoding the version object is convenient, but questionable. This is used by apply + // and rolling-update today, but both of those cases should probably be requesting the raw + // object and performing their own decoding. + obj, versioned := versions.Last(), versions.First() name, _ := mapping.MetadataAccessor.Name(obj) namespace, _ := mapping.MetadataAccessor.Namespace(obj) resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj) - var versionedObject interface{} - - if vo, _, err := api.Scheme.Raw().DecodeToVersionedObject(data); err == nil { - versionedObject = vo - } return &Info{ Mapping: mapping, Client: client, Namespace: namespace, Name: name, Source: source, - VersionedObject: versionedObject, + VersionedObject: versioned, Object: obj, ResourceVersion: resourceVersion, }, nil diff --git a/pkg/kubectl/resource/result.go b/pkg/kubectl/resource/result.go index be6b4d5e051..8d726ab7b73 100644 --- a/pkg/kubectl/resource/result.go +++ b/pkg/kubectl/resource/result.go @@ -210,8 +210,8 @@ func (r *Result) Watch(resourceVersion string) (watch.Interface, error) { // the objects as children, or if only a single Object is present, as that object. The provided // version will be preferred as the conversion target, but the Object's mapping version will be // used if that version is not present. -func AsVersionedObject(infos []*Info, forceList bool, version string) (runtime.Object, error) { - objects, err := AsVersionedObjects(infos, version) +func AsVersionedObject(infos []*Info, forceList bool, version string, encoder runtime.Encoder) (runtime.Object, error) { + objects, err := AsVersionedObjects(infos, version, encoder) if err != nil { return nil, err } @@ -233,19 +233,21 @@ func AsVersionedObject(infos []*Info, forceList bool, version string) (runtime.O // AsVersionedObjects converts a list of infos into versioned objects. The provided // version will be preferred as the conversion target, but the Object's mapping version will be // used if that version is not present. -func AsVersionedObjects(infos []*Info, version string) ([]runtime.Object, error) { +func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder) ([]runtime.Object, error) { objects := []runtime.Object{} for _, info := range infos { if info.Object == nil { continue } + // TODO: use info.VersionedObject as the value? + // objects that are not part of api.Scheme must be converted to JSON // TODO: convert to map[string]interface{}, attach to runtime.Unknown? if len(version) > 0 { if _, err := api.Scheme.ObjectKind(info.Object); runtime.IsNotRegisteredError(err) { // TODO: ideally this would encode to version, but we don't expose multiple codecs here. - data, err := info.Mapping.Codec.Encode(info.Object) + data, err := runtime.Encode(encoder, info.Object) if err != nil { return nil, err } diff --git a/pkg/kubectl/resource/visitor.go b/pkg/kubectl/resource/visitor.go index 5054a723852..a187919a414 100644 --- a/pkg/kubectl/resource/visitor.go +++ b/pkg/kubectl/resource/visitor.go @@ -345,7 +345,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error { if errs := runtime.DecodeList(items, struct { runtime.ObjectTyper runtime.Decoder - }{v.Mapper, info.Mapping.Codec}); len(errs) > 0 { + }{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 { return utilerrors.NewAggregate(errs) } for i := range items { diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index bd5a4266361..974cff0dfc4 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -19,7 +19,6 @@ package kubectl import ( "bytes" "encoding/json" - "errors" "fmt" "io" "io/ioutil" @@ -36,11 +35,11 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" + utilerrors "k8s.io/kubernetes/pkg/util/errors" "k8s.io/kubernetes/pkg/util/jsonpath" "k8s.io/kubernetes/pkg/util/sets" ) @@ -66,7 +65,10 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) { case "yaml": printer = &YAMLPrinter{} case "name": - printer = &NamePrinter{} + printer = &NamePrinter{ + Typer: runtime.ObjectTyperToTyper(api.Scheme), + Decoder: api.Codecs.UniversalDecoder(), + } case "template", "go-template": if len(formatArgument) == 0 { return nil, false, fmt.Errorf("template format specified but no template given") @@ -197,62 +199,47 @@ func (p *VersionedPrinter) HandledResources() []string { // NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object. type NamePrinter struct { + Decoder runtime.Decoder + Typer runtime.Typer } // PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object // and print "resource/name" pair. If the object is a List, print all items in it. func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { - objvalue := reflect.ValueOf(obj).Elem() - kindString := objvalue.FieldByName("Kind") - groupVersionString := objvalue.FieldByName("APIVersion") - kind := unversioned.GroupVersionKind{} - if !kindString.IsValid() { - kindString = reflect.ValueOf("") - } - kind.Kind = kindString.String() + gvk, _, _ := p.Typer.ObjectKind(obj) - if !groupVersionString.IsValid() { - groupVersionString = reflect.ValueOf("/") - } - gv, err := unversioned.ParseGroupVersion(groupVersionString.String()) - if err != nil { - kind.Group = gv.Group - kind.Version = gv.Version - } - - if kind.Kind == "List" { - items := objvalue.FieldByName("Items") - if items.Type().String() == "[]runtime.RawExtension" { - for i := 0; i < items.Len(); i++ { - rawObj := items.Index(i).FieldByName("RawJSON").Interface().([]byte) - scheme := api.Scheme - groupVersionKind, err := scheme.DataKind(rawObj) - if err != nil { - return err - } - decodedObj, err := scheme.DecodeToVersion(rawObj, unversioned.GroupVersion{}) - if err != nil { - return err - } - tpmeta := unversioned.TypeMeta{ - APIVersion: groupVersionKind.GroupVersion().String(), - Kind: groupVersionKind.Kind, - } - s := reflect.ValueOf(decodedObj).Elem() - s.FieldByName("TypeMeta").Set(reflect.ValueOf(tpmeta)) - p.PrintObj(decodedObj, w) + if meta.IsListType(obj) { + items, err := meta.ExtractList(obj) + if err != nil { + return err + } + if errs := runtime.DecodeList(items, p.Decoder, runtime.UnstructuredJSONScheme); len(errs) > 0 { + return utilerrors.NewAggregate(errs) + } + for _, obj := range items { + if err := p.PrintObj(obj, w); err != nil { + return err } - } else { - return errors.New("the list object contains unrecognized items.") } - } else { - name := objvalue.FieldByName("Name") - if !name.IsValid() { - name = reflect.ValueOf("") + return nil + } + + // TODO: this is wrong, runtime.Unknown and runtime.Unstructured are not handled properly here. + + name := "" + if acc, err := meta.Accessor(obj); err == nil { + if n := acc.GetName(); len(n) > 0 { + name = n } - _, resource := meta.KindToResource(kind, false) + } + + if gvk != nil { + // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper + _, resource := meta.KindToResource(*gvk, false) fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) + } else { + fmt.Fprintf(w, "/%s\n", name) } return nil @@ -1742,9 +1729,8 @@ func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) { // PrintObj formats the obj with the JSONPath Template. func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error { - var queryObj interface{} - switch obj.(type) { - case *v1.List, *api.List: + var queryObj interface{} = obj + if meta.IsListType(obj) { data, err := json.Marshal(obj) if err != nil { return err @@ -1753,8 +1739,6 @@ func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error { if err := json.Unmarshal(data, &queryObj); err != nil { return err } - default: - queryObj = obj } if err := j.JSONPath.Execute(w, queryObj); err != nil { diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index 1f71995a264..a60eae59292 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -33,6 +33,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing" "k8s.io/kubernetes/pkg/runtime" + yamlserializer "k8s.io/kubernetes/pkg/runtime/serializer/yaml" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/sets" @@ -121,17 +122,17 @@ func TestPrinter(t *testing.T) { {"test jsonpath", "jsonpath", "{.metadata.name}", podTest, "foo"}, {"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, "foo bar"}, {"test jsonpath empty list", "jsonpath", "{.items[*].metadata.name}", emptyListTest, ""}, - {"test name", "name", "", podTest, "/foo\n"}, + {"test name", "name", "", podTest, "pod/foo\n"}, {"emits versioned objects", "template", "{{.kind}}", testapi, "Pod"}, } for _, test := range printerTests { buf := bytes.NewBuffer([]byte{}) printer, found, err := GetPrinter(test.Format, test.FormatArgument) if err != nil || !found { - t.Errorf("unexpected error: %#v", err) + t.Errorf("in %s, unexpected error: %#v", test.Name, err) } if err := printer.PrintObj(test.Input, buf); err != nil { - t.Errorf("unexpected error: %#v", err) + t.Errorf("in %s, unexpected error: %#v", test.Name, err) } if buf.String() != test.Expect { t.Errorf("in %s, expect %q, got %q", test.Name, test.Expect, buf.String()) @@ -175,8 +176,8 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data } // Use real decode function to undo the versioning process. poutput = kubectltesting.TestStruct{} - err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &poutput) - if err != nil { + s := yamlserializer.NewDecodingSerializer(testapi.Default.Codec()) + if err := runtime.DecodeInto(s, buf.Bytes(), &poutput); err != nil { t.Fatal(err) } if !reflect.DeepEqual(testData, poutput) { @@ -196,8 +197,7 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data } // Use real decode function to undo the versioning process. objOut = api.Pod{} - err = runtime.YAMLDecoder(testapi.Default.Codec()).DecodeInto(buf.Bytes(), &objOut) - if err != nil { + if err := runtime.DecodeInto(s, buf.Bytes(), &objOut); err != nil { t.Fatal(err) } if !reflect.DeepEqual(obj, &objOut) { @@ -463,7 +463,10 @@ func TestPrinters(t *testing.T) { "template": templatePrinter, "template2": templatePrinter2, "jsonpath": jsonpathPrinter, - "name": &NamePrinter{}, + "name": &NamePrinter{ + Typer: runtime.ObjectTyperToTyper(api.Scheme), + Decoder: api.Codecs.UniversalDecoder(), + }, } objects := map[string]runtime.Object{ "pod": &api.Pod{ObjectMeta: om("pod")}, diff --git a/pkg/kubectl/sorting_printer.go b/pkg/kubectl/sorting_printer.go index c1f3c54ae45..49e36b882e9 100644 --- a/pkg/kubectl/sorting_printer.go +++ b/pkg/kubectl/sorting_printer.go @@ -22,7 +22,6 @@ import ( "reflect" "sort" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" @@ -36,6 +35,7 @@ import ( type SortingPrinter struct { SortField string Delegate ResourcePrinter + Decoder runtime.Decoder } func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error { @@ -63,7 +63,7 @@ func (s *SortingPrinter) sortObj(obj runtime.Object) error { return nil } - sorter, err := SortObjects(objs, s.SortField) + sorter, err := SortObjects(s.Decoder, objs, s.SortField) if err != nil { return err } @@ -80,7 +80,7 @@ func (s *SortingPrinter) sortObj(obj runtime.Object) error { return meta.SetList(obj, objs) } -func SortObjects(objs []runtime.Object, fieldInput string) (*RuntimeSort, error) { +func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) { parser := jsonpath.New("sorting") field, err := massageJSONPath(fieldInput) @@ -97,7 +97,7 @@ func SortObjects(objs []runtime.Object, fieldInput string) (*RuntimeSort, error) switch u := item.(type) { case *runtime.Unknown: var err error - if objs[ix], err = api.Codec.Decode(u.RawJSON); err != nil { + if objs[ix], _, err = decoder.Decode(u.RawJSON, nil, nil); err != nil { return nil, err } } diff --git a/pkg/kubectl/sorting_printer_test.go b/pkg/kubectl/sorting_printer_test.go index 8aed8a5b0af..f1d1d0c65cb 100644 --- a/pkg/kubectl/sorting_printer_test.go +++ b/pkg/kubectl/sorting_printer_test.go @@ -20,12 +20,13 @@ import ( "reflect" "testing" + internal "k8s.io/kubernetes/pkg/api" api "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/runtime" ) func encodeOrDie(obj runtime.Object) []byte { - data, err := api.Codec.Encode(obj) + data, err := runtime.Encode(internal.Codecs.LegacyCodec(api.SchemeGroupVersion), obj) if err != nil { panic(err.Error()) } @@ -223,7 +224,7 @@ func TestSortingPrinter(t *testing.T) { }, } for _, test := range tests { - sort := &SortingPrinter{SortField: test.field} + sort := &SortingPrinter{SortField: test.field, Decoder: internal.Codecs.UniversalDecoder()} if err := sort.sortObj(test.obj); err != nil { t.Errorf("unexpected error: %v (%s)", err, test.name) continue diff --git a/pkg/kubelet/config/common.go b/pkg/kubelet/config/common.go index 86318deb883..0838699ec26 100644 --- a/pkg/kubelet/config/common.go +++ b/pkg/kubelet/config/common.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/pkg/api/validation" "k8s.io/kubernetes/pkg/apimachinery/registered" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/hash" utilyaml "k8s.io/kubernetes/pkg/util/yaml" @@ -93,7 +94,7 @@ func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *a if err != nil { return false, nil, err } - obj, err := api.Scheme.Decode(json) + obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), json) if err != nil { return false, pod, err } @@ -115,17 +116,13 @@ func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *a } func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods api.PodList, err error) { - json, err := utilyaml.ToJSON(data) - if err != nil { - return false, api.PodList{}, err - } - obj, err := api.Scheme.Decode(json) + obj, err := runtime.Decode(api.Codecs.UniversalDecoder(), data) if err != nil { return false, pods, err } // Check whether the object could be converted to list of pods. if _, ok := obj.(*api.PodList); !ok { - err = fmt.Errorf("invalid pods list: %+v", obj) + err = fmt.Errorf("invalid pods list: %#v", obj) return false, pods, err } newPods := obj.(*api.PodList) diff --git a/pkg/kubelet/config/common_test.go b/pkg/kubelet/config/common_test.go index acdad1b6838..5265fca3675 100644 --- a/pkg/kubelet/config/common_test.go +++ b/pkg/kubelet/config/common_test.go @@ -24,9 +24,10 @@ import ( "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/securitycontext" - "github.com/ghodss/yaml" + //"github.com/ghodss/yaml" ) func noDefault(*api.Pod) error { return nil } @@ -56,7 +57,7 @@ func TestDecodeSinglePod(t *testing.T) { SecurityContext: &api.PodSecurityContext{}, }, } - json, err := testapi.Default.Codec().Encode(pod) + json, err := runtime.Encode(testapi.Default.Codec(), pod) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -72,15 +73,12 @@ func TestDecodeSinglePod(t *testing.T) { } for _, gv := range registered.EnabledVersionsForGroup(api.GroupName) { - externalPod, err := testapi.Default.Converter().ConvertToVersion(pod, gv.String()) + s, _ := api.Codecs.SerializerForFileExtension("yaml") + encoder := api.Codecs.EncoderForVersion(s, gv) + yaml, err := runtime.Encode(encoder, pod) if err != nil { t.Errorf("unexpected error: %v", err) } - yaml, err := yaml.Marshal(externalPod) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - parsed, podOut, err = tryDecodeSinglePod(yaml, noDefault) if !parsed { t.Errorf("expected to have parsed file: (%s)", string(yaml)) @@ -122,7 +120,7 @@ func TestDecodePodList(t *testing.T) { podList := &api.PodList{ Items: []api.Pod{*pod}, } - json, err := testapi.Default.Codec().Encode(podList) + json, err := runtime.Encode(testapi.Default.Codec(), podList) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -138,21 +136,21 @@ func TestDecodePodList(t *testing.T) { } for _, gv := range registered.EnabledVersionsForGroup(api.GroupName) { - externalPodList, err := testapi.Default.Converter().ConvertToVersion(podList, gv.String()) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - yaml, err := yaml.Marshal(externalPodList) + s, _ := api.Codecs.SerializerForFileExtension("yaml") + encoder := api.Codecs.EncoderForVersion(s, gv) + yaml, err := runtime.Encode(encoder, podList) if err != nil { t.Errorf("unexpected error: %v", err) } parsed, podListOut, err = tryDecodePodList(yaml, noDefault) if !parsed { - t.Errorf("expected to have parsed file: (%s)", string(yaml)) + t.Errorf("expected to have parsed file: (%s): %v", string(yaml), err) + continue } if err != nil { t.Errorf("unexpected error: %v (%s)", err, string(yaml)) + continue } if !reflect.DeepEqual(podList, &podListOut) { t.Errorf("expected:\n%#v\ngot:\n%#v\n%s", pod, &podListOut, string(yaml)) diff --git a/pkg/kubelet/config/file_test.go b/pkg/kubelet/config/file_test.go index 268985b2506..1b41f0729b7 100644 --- a/pkg/kubelet/config/file_test.go +++ b/pkg/kubelet/config/file_test.go @@ -132,7 +132,7 @@ func TestReadPodsFromFile(t *testing.T) { if err != nil { t.Fatalf("%s: error in versioning the pod: %v", testCase.desc, err) } - fileContents, err := testapi.Default.Codec().Encode(versionedPod) + fileContents, err := runtime.Encode(testapi.Default.Codec(), versionedPod) if err != nil { t.Fatalf("%s: error in encoding the pod: %v", testCase.desc, err) } diff --git a/pkg/kubelet/config/http_test.go b/pkg/kubelet/config/http_test.go index aa63f248775..14dab53176e 100644 --- a/pkg/kubelet/config/http_test.go +++ b/pkg/kubelet/config/http_test.go @@ -281,7 +281,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { if err != nil { t.Fatalf("%s: error in versioning the pods: %s", testCase.desc, err) } - data, err := testapi.Default.Codec().Encode(versionedPods) + data, err := runtime.Encode(testapi.Default.Codec(), versionedPods) if err != nil { t.Fatalf("%s: error in encoding the pod: %v", testCase.desc, err) } diff --git a/pkg/kubelet/dockertools/labels.go b/pkg/kubelet/dockertools/labels.go index 15535d97522..fd7ad7c8985 100644 --- a/pkg/kubelet/dockertools/labels.go +++ b/pkg/kubelet/dockertools/labels.go @@ -22,9 +22,9 @@ import ( "github.com/golang/glog" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apimachinery/registered" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/util/format" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" ) @@ -187,8 +187,7 @@ func supplyContainerInfoWithOldLabel(labels map[string]string, containerInfo *la return } pod = &api.Pod{} - err := registered.GroupOrDie(api.GroupName).Codec.DecodeInto([]byte(data), pod) - if err != nil { + if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), []byte(data), pod); err != nil { // If the pod label can't be parsed, we should report an error logError(containerInfo, kubernetesPodLabel, err) return diff --git a/pkg/kubelet/dockertools/labels_test.go b/pkg/kubelet/dockertools/labels_test.go index 1dfe24c4f83..41e8a4b1056 100644 --- a/pkg/kubelet/dockertools/labels_test.go +++ b/pkg/kubelet/dockertools/labels_test.go @@ -22,9 +22,10 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/api/testapi" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/util/format" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/intstr" ) @@ -107,7 +108,7 @@ func TestLabels(t *testing.T) { pod.DeletionGracePeriodSeconds = &deletionGracePeriod pod.Spec.TerminationGracePeriodSeconds = &terminationGracePeriod container.Lifecycle = lifecycle - data, err := registered.GroupOrDie(api.GroupName).Codec.Encode(pod) + data, err := runtime.Encode(testapi.Default.Codec(), pod) if err != nil { t.Fatalf("Failed to encode pod %q into string: %v", format.Pod(pod), err) } diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index f19a6566797..973f78facef 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -37,7 +37,6 @@ import ( cadvisorapi "github.com/google/cadvisor/info/v1" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/client/record" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/lifecycle" @@ -48,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/kubelet/qos" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/util/format" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/securitycontext" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" @@ -655,11 +655,12 @@ func (dm *DockerManager) runContainer( // TODO(random-liu): Remove this when we start to use new labels for KillContainerInPod if container.Lifecycle != nil && container.Lifecycle.PreStop != nil { // TODO: This is kind of hacky, we should really just encode the bits we need. - data, err := registered.GroupOrDie(api.GroupName).Codec.Encode(pod) - if err != nil { - glog.Errorf("Failed to encode pod: %s for prestop hook", pod.Name) - } else { + // TODO: This is hacky because the Kubelet should be parameterized to encode a specific version + // and needs to be able to migrate this whenever we deprecate v1. Should be a member of DockerManager. + if data, err := runtime.Encode(api.Codecs.LegacyCodec(unversioned.GroupVersion{Group: api.GroupName, Version: "v1"}), pod); err == nil { labels[kubernetesPodLabel] = string(data) + } else { + glog.Errorf("Failed to encode pod: %s for prestop hook", pod.Name) } } memoryLimit := container.Resources.Limits.Memory().Value() @@ -1432,7 +1433,7 @@ func containerAndPodFromLabels(inspect *docker.Container) (pod *api.Pod, contain // the pod data may not be set if body, found := labels[kubernetesPodLabel]; found { pod = &api.Pod{} - if err = registered.GroupOrDie(api.GroupName).Codec.DecodeInto([]byte(body), pod); err == nil { + if err = runtime.DecodeInto(api.Codecs.UniversalDecoder(), []byte(body), pod); err == nil { name := labels[kubernetesContainerNameLabel] for ix := range pod.Spec.Containers { if pod.Spec.Containers[ix].Name == name { diff --git a/pkg/kubelet/dockertools/manager_test.go b/pkg/kubelet/dockertools/manager_test.go index 4b8735a9b0d..ede3251da17 100644 --- a/pkg/kubelet/dockertools/manager_test.go +++ b/pkg/kubelet/dockertools/manager_test.go @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/kubelet/network" proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results" kubetypes "k8s.io/kubernetes/pkg/kubelet/types" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" uexec "k8s.io/kubernetes/pkg/util/exec" @@ -448,7 +449,7 @@ func TestKillContainerInPodWithPreStop(t *testing.T) { }, {Name: "bar"}}}, } - podString, err := testapi.Default.Codec().Encode(pod) + podString, err := runtime.Encode(testapi.Default.Codec(), pod) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/kubelet/server/server.go b/pkg/kubelet/server/server.go index 37c7f563d66..0f61d40b426 100644 --- a/pkg/kubelet/server/server.go +++ b/pkg/kubelet/server/server.go @@ -39,7 +39,6 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/validation" - "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/auth/authenticator" "k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/client/unversioned/remotecommand" @@ -48,6 +47,7 @@ import ( kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" "k8s.io/kubernetes/pkg/kubelet/server/portforward" "k8s.io/kubernetes/pkg/kubelet/server/stats" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util/flushwriter" @@ -402,18 +402,12 @@ func (s *Server) getContainerLogs(request *restful.Request, response *restful.Re delete(query, "tailLines") } } - // container logs on the kubelet are locked to v1 - versioned := &v1.PodLogOptions{} - if err := api.Scheme.Convert(&query, versioned); err != nil { + // container logs on the kubelet are locked to the v1 API version of PodLogOptions + logOptions := &api.PodLogOptions{} + if err := api.ParameterCodec.DecodeParameters(query, v1.SchemeGroupVersion, logOptions); err != nil { response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Unable to decode query."}`)) return } - out, err := api.Scheme.ConvertToVersion(versioned, "") - if err != nil { - response.WriteError(http.StatusBadRequest, fmt.Errorf(`{"message": "Unable to convert request query."}`)) - return - } - logOptions := out.(*api.PodLogOptions) logOptions.TypeMeta = unversioned.TypeMeta{} if errs := validation.ValidatePodLogOptions(logOptions); len(errs) > 0 { response.WriteError(apierrs.StatusUnprocessableEntity, fmt.Errorf(`{"message": "Invalid request."}`)) @@ -462,7 +456,11 @@ func encodePods(pods []*api.Pod) (data []byte, err error) { for _, pod := range pods { podList.Items = append(podList.Items, *pod) } - return registered.GroupOrDie(api.GroupName).Codec.Encode(podList) + // TODO: this needs to be parameterized to the kubelet, not hardcoded. Depends on Kubelet + // as API server refactor. + // TODO: Locked to v1, needs to be made generic + codec := api.Codecs.LegacyCodec(unversioned.GroupVersion{Group: api.GroupName, Version: "v1"}) + return runtime.Encode(codec, podList) } // getPods returns a list of pods bound to the Kubelet and their spec. diff --git a/pkg/master/master.go b/pkg/master/master.go index de8bc0925e9..64cb3ef2c7d 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -66,6 +66,7 @@ import ( thirdpartyresourceetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresource/etcd" "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata" thirdpartyresourcedataetcd "k8s.io/kubernetes/pkg/registry/thirdpartyresourcedata/etcd" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/storage" etcdutil "k8s.io/kubernetes/pkg/storage/etcd/util" "k8s.io/kubernetes/pkg/util" @@ -183,7 +184,10 @@ func (m *Master) InstallAPIs(c *Config) { VersionedResourcesStorageMap: map[string]map[string]rest.Storage{ "v1": m.v1ResourcesStorage, }, - IsLegacyGroup: true, + IsLegacyGroup: true, + Scheme: api.Scheme, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, } apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) } @@ -217,6 +221,9 @@ func (m *Master) InstallAPIs(c *Config) { "v1beta1": extensionResources, }, OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion, + Scheme: api.Scheme, + ParameterCodec: api.ParameterCodec, + NegotiatedSerializer: api.Codecs, } apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo) @@ -237,7 +244,7 @@ func (m *Master) InstallAPIs(c *Config) { // This should be done after all groups are registered // TODO: replace the hardcoded "apis". - apiserver.AddApisWebService(m.HandlerContainer, "/apis", func() []unversioned.APIGroup { + apiserver.AddApisWebService(m.Serializer, m.HandlerContainer, "/apis", func() []unversioned.APIGroup { groups := []unversioned.APIGroup{} for ix := range allGroups { groups = append(groups, allGroups[ix]) @@ -517,9 +524,9 @@ func (m *Master) InstallThirdPartyResource(rsrc *extensions.ThirdPartyResource) Name: group, Versions: []unversioned.GroupVersionForDiscovery{groupVersion}, } - apiserver.AddGroupWebService(m.HandlerContainer, path, apiGroup) + apiserver.AddGroupWebService(api.Codecs, m.HandlerContainer, path, apiGroup) m.addThirdPartyResourceStorage(path, thirdparty.Storage[strings.ToLower(kind)+"s"].(*thirdpartyresourcedataetcd.REST), apiGroup) - apiserver.InstallServiceErrorHandler(m.HandlerContainer, m.NewRequestInfoResolver(), []string{thirdparty.GroupVersion.String()}) + apiserver.InstallServiceErrorHandler(api.Codecs, m.HandlerContainer, m.NewRequestInfoResolver(), []string{thirdparty.GroupVersion.String()}) return nil } @@ -533,10 +540,12 @@ func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupV } optionsExternalVersion := registered.GroupOrDie(api.GroupName).GroupVersion + internalVersion := unversioned.GroupVersion{Group: group, Version: runtime.APIVersionInternal} + externalVersion := unversioned.GroupVersion{Group: group, Version: version} return &apiserver.APIGroupVersion{ Root: apiRoot, - GroupVersion: unversioned.GroupVersion{Group: group, Version: version}, + GroupVersion: externalVersion, RequestInfoResolver: m.NewRequestInfoResolver(), Creater: thirdpartyresourcedata.NewObjectCreator(group, version, api.Scheme), @@ -544,11 +553,13 @@ func (m *Master) thirdpartyapi(group, kind, version string) *apiserver.APIGroupV Typer: api.Scheme, Mapper: thirdpartyresourcedata.NewMapper(registered.GroupOrDie(extensions.GroupName).RESTMapper, kind, version, group), - Codec: thirdpartyresourcedata.NewCodec(registered.GroupOrDie(extensions.GroupName).Codec, kind), Linker: registered.GroupOrDie(extensions.GroupName).SelfLinker, Storage: storage, OptionsExternalVersion: &optionsExternalVersion, + Serializer: thirdpartyresourcedata.NewNegotiatedSerializer(api.Codecs, kind, externalVersion, internalVersion), + ParameterCodec: api.ParameterCodec, + Context: m.RequestContextMapper, MinRequestTimeout: m.MinRequestTimeout, diff --git a/pkg/master/master_test.go b/pkg/master/master_test.go index 9147f951283..b1996bd9604 100644 --- a/pkg/master/master_test.go +++ b/pkg/master/master_test.go @@ -83,6 +83,7 @@ func setUp(t *testing.T) (Master, *etcdtesting.EtcdTestServer, Config, *assert.A func newMaster(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) { _, etcdserver, config, assert := setUp(t) + config.Serializer = api.Codecs config.KubeletClient = client.FakeKubeletClient{} config.ProxyDialer = func(network, addr string) (net.Conn, error) { return nil, nil } @@ -496,7 +497,6 @@ func decodeResponse(resp *http.Response, obj interface{}) error { if err != nil { return err } - if err := json.Unmarshal(data, obj); err != nil { return err } diff --git a/pkg/registry/thirdpartyresourcedata/codec.go b/pkg/registry/thirdpartyresourcedata/codec.go index cfedc0565e0..33ee1094520 100644 --- a/pkg/registry/thirdpartyresourcedata/codec.go +++ b/pkg/registry/thirdpartyresourcedata/codec.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "io" - "net/url" "strings" "k8s.io/kubernetes/pkg/api" @@ -116,7 +115,6 @@ func (t *thirdPartyResourceDataMapper) RESTMapping(gk unversioned.GroupKind, ver if err != nil { return nil, err } - mapping.Codec = NewCodec(mapping.Codec, t.kind) return mapping, nil } @@ -141,6 +139,37 @@ func NewMapper(mapper meta.RESTMapper, kind, version, group string) meta.RESTMap } } +type thirdPartyResourceDataCodecFactory struct { + runtime.NegotiatedSerializer + kind string + encodeGV unversioned.GroupVersion + decodeGV unversioned.GroupVersion +} + +func NewNegotiatedSerializer(s runtime.NegotiatedSerializer, kind string, encodeGV, decodeGV unversioned.GroupVersion) runtime.NegotiatedSerializer { + return &thirdPartyResourceDataCodecFactory{ + NegotiatedSerializer: s, + + kind: kind, + encodeGV: encodeGV, + decodeGV: decodeGV, + } +} + +func (t *thirdPartyResourceDataCodecFactory) EncoderForVersion(s runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder { + return NewCodec(runtime.NewCodec( + t.NegotiatedSerializer.EncoderForVersion(s, gv), + t.NegotiatedSerializer.DecoderToVersion(s, t.decodeGV), + ), t.kind) +} + +func (t *thirdPartyResourceDataCodecFactory) DecoderToVersion(s runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder { + return NewCodec(runtime.NewCodec( + t.NegotiatedSerializer.EncoderForVersion(s, t.encodeGV), + t.NegotiatedSerializer.DecoderToVersion(s, gv), + ), t.kind) +} + type thirdPartyResourceDataCodec struct { delegate runtime.Codec kind string @@ -190,84 +219,77 @@ func (t *thirdPartyResourceDataCodec) populateFromObject(objIn *extensions.Third return nil } -func (t *thirdPartyResourceDataCodec) Decode(data []byte) (runtime.Object, error) { - result := &extensions.ThirdPartyResourceData{} - if err := t.populate(result, data); err != nil { - return nil, err +func (t *thirdPartyResourceDataCodec) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { + if into == nil { + obj := &extensions.ThirdPartyResourceData{} + if err := t.populate(obj, data); err != nil { + return nil, nil, err + } + objData, err := runtime.Encode(t.delegate, obj) + if err != nil { + return nil, nil, err + } + return t.delegate.Decode(objData, gvk, into) } - return result, nil -} -func (t *thirdPartyResourceDataCodec) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (runtime.Object, error) { - // TODO: this is hacky, there must be a better way... - obj, err := runtime.Decode(t, data) - if err != nil { - return nil, err - } - objData, err := t.delegate.Encode(obj) - if err != nil { - return nil, err - } - return t.delegate.DecodeToVersion(objData, gv) -} - -func (t *thirdPartyResourceDataCodec) DecodeInto(data []byte, obj runtime.Object) error { - thirdParty, ok := obj.(*extensions.ThirdPartyResourceData) + thirdParty, ok := into.(*extensions.ThirdPartyResourceData) if !ok { - return fmt.Errorf("unexpected object: %#v", obj) - } - return t.populate(thirdParty, data) -} - -func (t *thirdPartyResourceDataCodec) DecodeIntoWithSpecifiedVersionKind(data []byte, obj runtime.Object, gvk unversioned.GroupVersionKind) error { - thirdParty, ok := obj.(*extensions.ThirdPartyResourceData) - if !ok { - return fmt.Errorf("unexpected object: %#v", obj) - } - - if gvk.Kind != "ThirdPartyResourceData" { - return fmt.Errorf("unexpeceted kind: %s", gvk.Kind) + return nil, nil, fmt.Errorf("unexpected object: %#v", into) } var dataObj interface{} if err := json.Unmarshal(data, &dataObj); err != nil { - return err + return nil, nil, err } mapObj, ok := dataObj.(map[string]interface{}) if !ok { - return fmt.Errorf("unexpcted object: %#v", dataObj) + return nil, nil, fmt.Errorf("unexpcted object: %#v", dataObj) } + /*if gvk.Kind != "ThirdPartyResourceData" { + return nil, nil, fmt.Errorf("unexpected kind: %s", gvk.Kind) + }*/ + actual := &unversioned.GroupVersionKind{} if kindObj, found := mapObj["kind"]; !found { + if gvk == nil { + return nil, nil, runtime.NewMissingKindErr(string(data)) + } mapObj["kind"] = gvk.Kind + actual.Kind = gvk.Kind } else { kindStr, ok := kindObj.(string) if !ok { - return fmt.Errorf("unexpected object for 'kind': %v", kindObj) + return nil, nil, fmt.Errorf("unexpected object for 'kind': %v", kindObj) } if kindStr != t.kind { - return fmt.Errorf("kind doesn't match, expecting: %s, got %s", gvk.Kind, kindStr) + return nil, nil, fmt.Errorf("kind doesn't match, expecting: %s, got %s", gvk.Kind, kindStr) } + actual.Kind = t.kind } if versionObj, found := mapObj["apiVersion"]; !found { + if gvk == nil { + return nil, nil, runtime.NewMissingVersionErr(string(data)) + } mapObj["apiVersion"] = gvk.GroupVersion().String() + actual.Group, actual.Version = gvk.Group, gvk.Version } else { versionStr, ok := versionObj.(string) if !ok { - return fmt.Errorf("unexpected object for 'apiVersion': %v", versionObj) + return nil, nil, fmt.Errorf("unexpected object for 'apiVersion': %v", versionObj) } - if versionStr != gvk.GroupVersion().String() { - return fmt.Errorf("version doesn't match, expecting: %v, got %s", gvk.GroupVersion(), versionStr) + if gvk != nil && versionStr != gvk.GroupVersion().String() { + return nil, nil, fmt.Errorf("version doesn't match, expecting: %v, got %s", gvk.GroupVersion(), versionStr) } + gv, err := unversioned.ParseGroupVersion(versionStr) + if err != nil { + return nil, nil, err + } + actual.Group, actual.Version = gv.Group, gv.Version } if err := t.populate(thirdParty, data); err != nil { - return err + return nil, actual, err } - return nil -} - -func (t *thirdPartyResourceDataCodec) DecodeParametersInto(parameters url.Values, obj runtime.Object) error { - return t.delegate.DecodeParametersInto(parameters, obj) + return thirdParty, actual, nil } const template = `{ @@ -289,15 +311,7 @@ func encodeToJSON(obj *extensions.ThirdPartyResourceData, stream io.Writer) erro return encoder.Encode(objMap) } -func (t *thirdPartyResourceDataCodec) Encode(obj runtime.Object) ([]byte, error) { - buff := &bytes.Buffer{} - if err := t.EncodeToStream(obj, buff); err != nil { - return nil, err - } - return buff.Bytes(), nil -} - -func (t *thirdPartyResourceDataCodec) EncodeToStream(obj runtime.Object, stream io.Writer) (err error) { +func (t *thirdPartyResourceDataCodec) EncodeToStream(obj runtime.Object, stream io.Writer, overrides ...unversioned.GroupVersion) (err error) { switch obj := obj.(type) { case *extensions.ThirdPartyResourceData: return encodeToJSON(obj, stream) @@ -315,7 +329,7 @@ func (t *thirdPartyResourceDataCodec) EncodeToStream(obj runtime.Object, stream fmt.Fprintf(stream, template, t.kind+"List", strings.Join(dataStrings, ",")) return nil case *unversioned.Status: - return t.delegate.EncodeToStream(obj, stream) + return t.delegate.EncodeToStream(obj, stream, overrides...) default: return fmt.Errorf("unexpected object to encode: %#v", obj) } diff --git a/pkg/registry/thirdpartyresourcedata/codec_test.go b/pkg/registry/thirdpartyresourcedata/codec_test.go index 5af08ce82e3..2fdd4e9c4b9 100644 --- a/pkg/registry/thirdpartyresourcedata/codec_test.go +++ b/pkg/registry/thirdpartyresourcedata/codec_test.go @@ -23,6 +23,7 @@ import ( "time" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/extensions" @@ -86,13 +87,13 @@ func TestCodec(t *testing.T) { }, } for _, test := range tests { - codec := thirdPartyResourceDataCodec{kind: "Foo"} + codec := &thirdPartyResourceDataCodec{kind: "Foo", delegate: testapi.Extensions.Codec()} data, err := json.Marshal(test.obj) if err != nil { t.Errorf("[%s] unexpected error: %v", test.name, err) continue } - obj, err := runtime.Decode(&codec, data) + obj, err := runtime.Decode(codec, data) if err != nil && !test.expectErr { t.Errorf("[%s] unexpected error: %v", test.name, err) continue @@ -120,7 +121,7 @@ func TestCodec(t *testing.T) { t.Errorf("[%s]\nexpected\n%v\nsaw\n%v\n", test.name, test.obj, &output) } - data, err = runtime.Encode(&codec, rsrcObj) + data, err = runtime.Encode(codec, rsrcObj) if err != nil { t.Errorf("[%s] unexpected error: %v", test.name, err) } diff --git a/pkg/runtime/codec.go b/pkg/runtime/codec.go index 3539388a4c3..bde0ae9755d 100644 --- a/pkg/runtime/codec.go +++ b/pkg/runtime/codec.go @@ -17,99 +17,155 @@ limitations under the License. package runtime import ( + "bytes" + "fmt" "io" + "net/url" + "reflect" "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/util/yaml" + "k8s.io/kubernetes/pkg/conversion/queryparams" ) +// codec binds an encoder and decoder. +type codec struct { + Encoder + Decoder +} + +// NewCodec creates a Codec from an Encoder and Decoder. +func NewCodec(e Encoder, d Decoder) Codec { + return codec{e, d} +} + // Encode is a convenience wrapper for encoding to a []byte from an Encoder -// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered. -func Encode(e Encoder, obj Object) ([]byte, error) { - return e.Encode(obj) +func Encode(e Encoder, obj Object, overrides ...unversioned.GroupVersion) ([]byte, error) { + // TODO: reuse buffer + buf := &bytes.Buffer{} + if err := e.EncodeToStream(obj, buf, overrides...); err != nil { + return nil, err + } + return buf.Bytes(), nil } // Decode is a convenience wrapper for decoding data into an Object. -// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered. func Decode(d Decoder, data []byte) (Object, error) { - return d.Decode(data) + obj, _, err := d.Decode(data, nil, nil) + return obj, err } // DecodeInto performs a Decode into the provided object. -// TODO: these are transitional interfaces to reduce refactor cost as Codec is altered. func DecodeInto(d Decoder, data []byte, into Object) error { - return d.DecodeInto(data, into) -} - -// CodecFor returns a Codec that invokes Encode with the provided version. -func CodecFor(codec ObjectCodec, version unversioned.GroupVersion) Codec { - return &codecWrapper{codec, version} -} - -// yamlCodec converts YAML passed to the Decoder methods to JSON. -type yamlCodec struct { - // a Codec for JSON - Codec -} - -// yamlCodec implements Codec -var _ Codec = yamlCodec{} -var _ Decoder = yamlCodec{} - -// YAMLDecoder adds YAML decoding support to a codec that supports JSON. -func YAMLDecoder(codec Codec) Codec { - return &yamlCodec{codec} -} - -func (c yamlCodec) Decode(data []byte) (Object, error) { - out, err := yaml.ToJSON(data) - if err != nil { - return nil, err - } - data = out - return c.Codec.Decode(data) -} - -func (c yamlCodec) DecodeInto(data []byte, obj Object) error { - out, err := yaml.ToJSON(data) + out, gvk, err := d.Decode(data, nil, into) if err != nil { return err } - data = out - return c.Codec.DecodeInto(data, obj) + if out != into { + return fmt.Errorf("unable to decode %s into %v", gvk, reflect.TypeOf(into)) + } + return nil } // EncodeOrDie is a version of Encode which will panic instead of returning an error. For tests. -func EncodeOrDie(codec Codec, obj Object) string { - bytes, err := Encode(codec, obj) +func EncodeOrDie(e Encoder, obj Object) string { + bytes, err := Encode(e, obj) if err != nil { panic(err) } return string(bytes) } -// codecWrapper implements encoding to an alternative -// default version for a scheme. -type codecWrapper struct { - ObjectCodec - version unversioned.GroupVersion +// UseOrCreateObject returns obj if the canonical ObjectKind returned by the provided typer matches gvk, or +// invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object. +func UseOrCreateObject(t Typer, c ObjectCreater, gvk unversioned.GroupVersionKind, obj Object) (Object, error) { + if obj != nil { + into, _, err := t.ObjectKind(obj) + if err != nil { + return nil, err + } + if gvk == *into { + return obj, nil + } + } + return c.New(gvk) } -// codecWrapper implements Decoder -var _ Decoder = &codecWrapper{} - -// Encode implements Codec -func (c *codecWrapper) Encode(obj Object) ([]byte, error) { - return c.EncodeToVersion(obj, c.version.String()) +// NoopEncoder converts an Decoder to a Serializer or Codec for code that expects them but only uses decoding. +type NoopEncoder struct { + Decoder } -func (c *codecWrapper) EncodeToStream(obj Object, stream io.Writer) error { - return c.EncodeToVersionStream(obj, c.version.String(), stream) +var _ Serializer = NoopEncoder{} + +func (n NoopEncoder) EncodeToStream(obj Object, w io.Writer, overrides ...unversioned.GroupVersion) error { + return fmt.Errorf("encoding is not allowed for this codec: %v", reflect.TypeOf(n.Decoder)) } -// TODO: Make this behaviour default when we move everyone away from -// the unversioned types. -// -// func (c *codecWrapper) Decode(data []byte) (Object, error) { -// return c.DecodeToVersion(data, c.version) -// } +// NoopDecoder converts an Encoder to a Serializer or Codec for code that expects them but only uses encoding. +type NoopDecoder struct { + Encoder +} + +var _ Serializer = NoopDecoder{} + +func (n NoopDecoder) Decode(data []byte, gvk *unversioned.GroupVersionKind, into Object) (Object, *unversioned.GroupVersionKind, error) { + return nil, nil, fmt.Errorf("decoding is not allowed for this codec: %v", reflect.TypeOf(n.Encoder)) +} + +// NewParameterCodec creates a ParameterCodec capable of transforming url values into versioned objects and back. +func NewParameterCodec(scheme *Scheme) ParameterCodec { + return ¶meterCodec{ + typer: ObjectTyperToTyper(scheme), + convertor: scheme, + creator: scheme, + } +} + +// parameterCodec implements conversion to and from query parameters and objects. +type parameterCodec struct { + typer Typer + convertor ObjectConvertor + creator ObjectCreater +} + +var _ ParameterCodec = ¶meterCodec{} + +// DecodeParameters converts the provided url.Values into an object of type From with the kind of into, and then +// converts that object to into (if necessary). Returns an error if the operation cannot be completed. +func (c *parameterCodec) DecodeParameters(parameters url.Values, from unversioned.GroupVersion, into Object) error { + if len(parameters) == 0 { + return nil + } + targetGVK, _, err := c.typer.ObjectKind(into) + if err != nil { + return err + } + if targetGVK.GroupVersion() == from { + return c.convertor.Convert(¶meters, into) + } + input, err := c.creator.New(from.WithKind(targetGVK.Kind)) + if err != nil { + return err + } + if err := c.convertor.Convert(¶meters, input); err != nil { + return err + } + return c.convertor.Convert(input, into) +} + +// EncodeParameters converts the provided object into the to version, then converts that object to url.Values. +// Returns an error if conversion is not possible. +func (c *parameterCodec) EncodeParameters(obj Object, to unversioned.GroupVersion) (url.Values, error) { + gvk, _, err := c.typer.ObjectKind(obj) + if err != nil { + return nil, err + } + if to != gvk.GroupVersion() { + out, err := c.convertor.ConvertToVersion(obj, to.String()) + if err != nil { + return nil, err + } + obj = out + } + return queryparams.Convert(obj) +} diff --git a/pkg/runtime/conversion_generator.go b/pkg/runtime/conversion_generator.go index 54e2f0a9cc8..6d70224e9be 100644 --- a/pkg/runtime/conversion_generator.go +++ b/pkg/runtime/conversion_generator.go @@ -102,10 +102,8 @@ func (g *conversionGenerator) AddImport(pkg string) string { func (g *conversionGenerator) GenerateConversionsForType(gv unversioned.GroupVersion, reflection reflect.Type) error { kind := reflection.Name() // TODO this is equivalent to what it did before, but it needs to be fixed for the proper group - internalVersion, exists := g.scheme.InternalVersions[gv.Group] - if !exists { - return fmt.Errorf("no internal version for %v", gv) - } + internalVersion := gv + internalVersion.Version = APIVersionInternal internalObj, err := g.scheme.NewObject(internalVersion.WithKind(kind)) if err != nil { @@ -775,6 +773,10 @@ func (g *conversionGenerator) writeConversionForStruct(b *buffer, inType, outTyp continue } + if g.scheme.Converter().IsConversionIgnored(inField.Type, outField.Type) { + continue + } + existsConversion := g.scheme.Converter().HasConversionFunc(inField.Type, outField.Type) _, hasPublicConversion := g.publicFuncs[typePair{inField.Type, outField.Type}] // TODO: This allows a private conversion for a slice to take precedence over a public @@ -895,12 +897,7 @@ type typePair struct { outType reflect.Type } -var defaultConversions []typePair = []typePair{ - {reflect.TypeOf([]RawExtension{}), reflect.TypeOf([]Object{})}, - {reflect.TypeOf([]Object{}), reflect.TypeOf([]RawExtension{})}, - {reflect.TypeOf(RawExtension{}), reflect.TypeOf(EmbeddedObject{})}, - {reflect.TypeOf(EmbeddedObject{}), reflect.TypeOf(RawExtension{})}, -} +var defaultConversions []typePair = []typePair{} func (g *conversionGenerator) OverwritePackage(pkg, overwrite string) { g.pkgOverwrites[pkg] = overwrite diff --git a/pkg/runtime/conversion_test.go b/pkg/runtime/conversion_test.go index cf31550839a..6105e5aad5a 100644 --- a/pkg/runtime/conversion_test.go +++ b/pkg/runtime/conversion_test.go @@ -46,12 +46,11 @@ func (obj *InternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj func (obj *ExternalComplex) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func TestStringMapConversion(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "external"} scheme := runtime.NewScheme() scheme.Log(t) - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{}) diff --git a/pkg/runtime/embedded.go b/pkg/runtime/embedded.go new file mode 100644 index 00000000000..0934d6837c2 --- /dev/null +++ b/pkg/runtime/embedded.go @@ -0,0 +1,124 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package runtime + +import ( + "errors" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/conversion" +) + +type encodable struct { + e Encoder `json:"-"` + obj Object + versions []unversioned.GroupVersion `json:"-"` +} + +func (e encodable) GetObjectKind() unversioned.ObjectKind { return e.obj.GetObjectKind() } + +// NewEncodable creates an object that will be encoded with the provided codec on demand. +// Provided as a convenience for test cases dealing with internal objects. +func NewEncodable(e Encoder, obj Object, versions ...unversioned.GroupVersion) Object { + if _, ok := obj.(*Unknown); ok { + return obj + } + return encodable{e, obj, versions} +} + +func (re encodable) UnmarshalJSON(in []byte) error { + return errors.New("runtime.encodable cannot be unmarshalled from JSON") +} + +// Marshal may get called on pointers or values, so implement MarshalJSON on value. +// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go +func (re encodable) MarshalJSON() ([]byte, error) { + return Encode(re.e, re.obj) +} + +// NewEncodableList creates an object that will be encoded with the provided codec on demand. +// Provided as a convenience for test cases dealing with internal objects. +func NewEncodableList(e Encoder, objects []Object, versions ...unversioned.GroupVersion) []Object { + out := make([]Object, len(objects)) + for i := range objects { + if _, ok := objects[i].(*Unknown); ok { + out[i] = objects[i] + continue + } + out[i] = NewEncodable(e, objects[i], versions...) + } + return out +} + +func (re *Unknown) UnmarshalJSON(in []byte) error { + if re == nil { + return errors.New("runtime.Unknown: UnmarshalJSON on nil pointer") + } + re.TypeMeta = TypeMeta{} + re.RawJSON = append(re.RawJSON[0:0], in...) + return nil +} + +// Marshal may get called on pointers or values, so implement MarshalJSON on value. +// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go +func (re Unknown) MarshalJSON() ([]byte, error) { + if re.RawJSON == nil { + return []byte("null"), nil + } + return re.RawJSON, nil +} + +func DefaultEmbeddedConversions() []interface{} { + return []interface{}{ + func(in *Object, out *RawExtension, s conversion.Scope) error { + if in == nil { + out.RawJSON = []byte("null") + return nil + } + obj := *in + if unk, ok := obj.(*Unknown); ok { + if unk.RawJSON != nil { + out.RawJSON = unk.RawJSON + return nil + } + obj = out.Object + } + if obj == nil { + out.RawJSON = nil + return nil + } + out.Object = obj + return nil + }, + + func(in *RawExtension, out *Object, s conversion.Scope) error { + if in.Object != nil { + *out = in.Object + return nil + } + data := in.RawJSON + if len(data) == 0 || (len(data) == 4 && string(data) == "null") { + *out = nil + return nil + } + *out = &Unknown{ + RawJSON: data, + } + return nil + }, + } +} diff --git a/pkg/runtime/embedded_test.go b/pkg/runtime/embedded_test.go index c0b9c867f99..64d6d74fb94 100644 --- a/pkg/runtime/embedded_test.go +++ b/pkg/runtime/embedded_test.go @@ -19,20 +19,21 @@ package runtime_test import ( "encoding/json" "reflect" + "strings" "testing" - "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" "k8s.io/kubernetes/pkg/util" ) type EmbeddedTest struct { runtime.TypeMeta ID string - Object runtime.EmbeddedObject - EmptyObject runtime.EmbeddedObject + Object runtime.Object + EmptyObject runtime.Object } type EmbeddedTestExternal struct { @@ -62,16 +63,17 @@ func (obj *EmbeddedTest) GetObjectKind() unversioned.ObjectKind { return func (obj *EmbeddedTestExternal) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} externalGVK := externalGV.WithKind("ObjectTest") s := runtime.NewScheme() - s.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{}) - obj, err := s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","items":[{}]}`)) + codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) + + obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -79,42 +81,51 @@ func TestDecodeEmptyRawExtensionAsObject(t *testing.T) { if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != "{}" { t.Fatalf("unexpected object: %#v", test.Items[0]) } + if *gvk != externalGVK { + t.Fatalf("unexpected kind: %#v", gvk) + } - obj, err = s.Decode([]byte(`{"kind":"` + externalGVK.Kind + `","apiVersion":"` + externalGV.String() + `","items":[{"kind":"Other","apiVersion":"v1"}]}`)) + obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } test = obj.(*ObjectTest) - if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "Other" || unk.APIVersion != "v1" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` { + if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.RawJSON) != `{"kind":"Other","apiVersion":"v1"}` { t.Fatalf("unexpected object: %#v", test.Items[0]) } + if *gvk != externalGVK { + t.Fatalf("unexpected kind: %#v", gvk) + } } func TestArrayOfRuntimeObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} s := runtime.NewScheme() - s.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{}) s.AddKnownTypes(internalGV, &ObjectTest{}) s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{}) - internal := &ObjectTest{ - Items: []runtime.Object{ - &EmbeddedTest{ID: "foo"}, - &EmbeddedTest{ID: "bar"}, - // TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization - &runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)}, - &ObjectTest{ - Items: []runtime.Object{ - &EmbeddedTest{ID: "baz"}, - }, - }, + codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) + + innerItems := []runtime.Object{ + &EmbeddedTest{ID: "baz"}, + } + items := []runtime.Object{ + &EmbeddedTest{ID: "foo"}, + &EmbeddedTest{ID: "bar"}, + // TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization + &runtime.Unknown{RawJSON: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`)}, + &ObjectTest{ + Items: runtime.NewEncodableList(codec, innerItems), }, } - wire, err := s.EncodeToVersion(internal, externalGV.String()) + internal := &ObjectTest{ + Items: runtime.NewEncodableList(codec, items), + } + wire, err := runtime.Encode(codec, internal) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -126,7 +137,10 @@ func TestArrayOfRuntimeObject(t *testing.T) { } t.Logf("exact wire is: %s", string(obj.Items[0].RawJSON)) - decoded, err := runtime.Decode(s, wire) + items[3] = &ObjectTest{Items: innerItems} + internal.Items = items + + decoded, err := runtime.Decode(codec, wire) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -134,7 +148,7 @@ func TestArrayOfRuntimeObject(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if errs := runtime.DecodeList(list, s); len(errs) > 0 { + if errs := runtime.DecodeList(list, codec); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } @@ -142,53 +156,65 @@ func TestArrayOfRuntimeObject(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if errs := runtime.DecodeList(list2, s); len(errs) > 0 { + if errs := runtime.DecodeList(list2, codec); len(errs) > 0 { t.Fatalf("unexpected error: %v", errs) } if err := meta.SetList(list[3], list2); err != nil { t.Fatalf("unexpected error: %v", err) } - internal.Items[2].(*runtime.Unknown).Kind = "OtherTest" - internal.Items[2].(*runtime.Unknown).APIVersion = "unknown.group/unknown" + // we want DecodeList to set type meta if possible, even on runtime.Unknown objects + internal.Items[2].(*runtime.Unknown).TypeMeta = runtime.TypeMeta{Kind: "OtherTest", APIVersion: "unknown.group/unknown"} if e, a := internal.Items, list; !reflect.DeepEqual(e, a) { - t.Errorf("mismatched decoded: %s", util.ObjectDiff(e, a)) + t.Errorf("mismatched decoded: %s", util.ObjectGoPrintSideBySide(e, a)) } } -func TestEmbeddedObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} +func TestNestedObject(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") s := runtime.NewScheme() - s.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) + codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) + + inner := &EmbeddedTest{ + ID: "inner", + } outer := &EmbeddedTest{ - ID: "outer", - Object: runtime.EmbeddedObject{ - Object: &EmbeddedTest{ - ID: "inner", - }, - }, + ID: "outer", + Object: runtime.NewEncodable(codec, inner), } - wire, err := s.EncodeToVersion(outer, externalGV.String()) + wire, err := runtime.Encode(codec, outer) if err != nil { t.Fatalf("Unexpected encode error '%v'", err) } t.Logf("Wire format is:\n%v\n", string(wire)) - decoded, err := runtime.Decode(s, wire) + decoded, err := runtime.Decode(codec, wire) if err != nil { t.Fatalf("Unexpected decode error %v", err) } + // for later tests + outer.Object = inner + + if e, a := outer, decoded; reflect.DeepEqual(e, a) { + t.Errorf("Expected unequal %#v %#v", e, a) + } + + obj, err := runtime.Decode(codec, decoded.(*EmbeddedTest).Object.(*runtime.Unknown).RawJSON) + if err != nil { + t.Fatal(err) + } + decoded.(*EmbeddedTest).Object = obj if e, a := outer, decoded; !reflect.DeepEqual(e, a) { - t.Errorf("Expected: %#v but got %#v", e, a) + t.Errorf("Expected equal %#v %#v", e, a) } // test JSON decoding of the external object, which should preserve @@ -211,46 +237,45 @@ func TestEmbeddedObject(t *testing.T) { // the external representation var decodedViaJSON EmbeddedTest err = json.Unmarshal(wire, &decodedViaJSON) - if err != nil { + if err == nil || !strings.Contains(err.Error(), "unmarshal object into Go value of type runtime.Object") { t.Fatalf("Unexpected decode error %v", err) } - if a := decodedViaJSON; a.Object.Object != nil || a.EmptyObject.Object != nil { + if a := decodedViaJSON; a.Object != nil || a.EmptyObject != nil { t.Errorf("Expected embedded objects to be nil: %#v", a) } } -// TestDeepCopyOfEmbeddedObject checks to make sure that EmbeddedObject's can be passed through DeepCopy with fidelity -func TestDeepCopyOfEmbeddedObject(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} +// TestDeepCopyOfRuntimeObject checks to make sure that runtime.Objects's can be passed through DeepCopy with fidelity +func TestDeepCopyOfRuntimeObject(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "v1test"} embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest") s := runtime.NewScheme() - s.AddInternalGroupVersion(internalGV) s.AddKnownTypes(internalGV, &EmbeddedTest{}) s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{}) original := &EmbeddedTest{ ID: "outer", - Object: runtime.EmbeddedObject{ - Object: &EmbeddedTest{ - ID: "inner", - }, + Object: &EmbeddedTest{ + ID: "inner", }, } - originalData, err := s.EncodeToVersion(original, externalGV.String()) + codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV) + + originalData, err := runtime.Encode(codec, original) if err != nil { t.Errorf("unexpected error: %v", err) } t.Logf("originalRole = %v\n", string(originalData)) - copyOfOriginal, err := api.Scheme.DeepCopy(original) + copyOfOriginal, err := s.DeepCopy(original) if err != nil { t.Fatalf("unexpected error: %v", err) } - copiedData, err := s.EncodeToVersion(copyOfOriginal.(runtime.Object), externalGV.String()) + copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object)) if err != nil { t.Errorf("unexpected error: %v", err) } diff --git a/pkg/runtime/error.go b/pkg/runtime/error.go index 40fa2437a6d..cbdc9b66105 100644 --- a/pkg/runtime/error.go +++ b/pkg/runtime/error.go @@ -37,3 +37,11 @@ func IsMissingKind(err error) bool { func IsMissingVersion(err error) bool { return conversion.IsMissingVersion(err) } + +func NewMissingKindErr(data string) error { + return conversion.NewMissingKindErr(data) +} + +func NewMissingVersionErr(data string) error { + return conversion.NewMissingVersionErr(data) +} diff --git a/pkg/runtime/extension.go b/pkg/runtime/extension.go index 2194ea3a9e9..629f675b69e 100644 --- a/pkg/runtime/extension.go +++ b/pkg/runtime/extension.go @@ -16,7 +16,10 @@ limitations under the License. package runtime -import "errors" +import ( + "encoding/json" + "errors" +) func (re *RawExtension) UnmarshalJSON(in []byte) error { if re == nil { @@ -29,5 +32,16 @@ func (re *RawExtension) UnmarshalJSON(in []byte) error { // Marshal may get called on pointers or values, so implement MarshalJSON on value. // http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go func (re RawExtension) MarshalJSON() ([]byte, error) { + if re.RawJSON == nil { + // TODO: this is to support legacy behavior of JSONPrinter and YAMLPrinter, which + // expect to call json.Marshal on arbitrary versioned objects (even those not in + // the scheme). pkg/kubectl/resource#AsVersionedObjects and its interaction with + // kubectl get on objects not in the scheme needs to be updated to ensure that the + // objects that are not part of the scheme are correctly put into the right form. + if re.Object != nil { + return json.Marshal(re.Object) + } + return []byte("null"), nil + } return re.RawJSON, nil } diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index 9b6c57cd659..4a76e81dc9f 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -22,8 +22,30 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" + "k8s.io/kubernetes/pkg/util/errors" ) +type objectTyperToTyper struct { + typer ObjectTyper +} + +func (t objectTyperToTyper) ObjectKind(obj Object) (*unversioned.GroupVersionKind, bool, error) { + gvk, err := t.typer.ObjectKind(obj) + if err != nil { + return nil, false, err + } + unversionedType, ok := t.typer.IsUnversioned(obj) + if !ok { + // ObjectTyper violates its contract + return nil, false, fmt.Errorf("typer returned a kind for %v, but then reported it was not in the scheme with IsUnversioned", reflect.TypeOf(obj)) + } + return &gvk, unversionedType, nil +} + +func ObjectTyperToTyper(typer ObjectTyper) Typer { + return objectTyperToTyper{typer: typer} +} + // fieldPtr puts the address of fieldName, which must be a member of v, // into dest, which must be an address of a variable to which this field's // address can be assigned. @@ -48,32 +70,56 @@ func FieldPtr(v reflect.Value, fieldName string, dest interface{}) error { return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), v.Type()) } +// EncodeList ensures that each object in an array is converted to a Unknown{} in serialized form. +// TODO: accept a content type. +func EncodeList(e Encoder, objects []Object, overrides ...unversioned.GroupVersion) error { + var errs []error + for i := range objects { + data, err := Encode(e, objects[i], overrides...) + if err != nil { + errs = append(errs, err) + continue + } + objects[i] = &Unknown{RawJSON: data} + } + return errors.NewAggregate(errs) +} + +func decodeListItem(obj *Unknown, decoders []Decoder) (Object, error) { + for _, decoder := range decoders { + obj, err := Decode(decoder, obj.RawJSON) + if err != nil { + if IsNotRegisteredError(err) { + continue + } + return nil, err + } + return obj, nil + } + // could not decode, so leave the object as Unknown, but give the decoders the + // chance to set Unknown.TypeMeta if it is available. + for _, decoder := range decoders { + if err := DecodeInto(decoder, obj.RawJSON, obj); err == nil { + return obj, nil + } + } + return obj, nil +} + // DecodeList alters the list in place, attempting to decode any objects found in -// the list that have the runtime.Unknown type. Any errors that occur are returned +// the list that have the Unknown type. Any errors that occur are returned // after the entire list is processed. Decoders are tried in order. -func DecodeList(objects []Object, decoders ...ObjectDecoder) []error { +func DecodeList(objects []Object, decoders ...Decoder) []error { errs := []error(nil) for i, obj := range objects { switch t := obj.(type) { case *Unknown: - for _, decoder := range decoders { - gv, err := unversioned.ParseGroupVersion(t.APIVersion) - if err != nil { - errs = append(errs, err) - break - } - - if !decoder.Recognizes(gv.WithKind(t.Kind)) { - continue - } - obj, err := Decode(decoder, t.RawJSON) - if err != nil { - errs = append(errs, err) - break - } - objects[i] = obj + decoded, err := decodeListItem(t, decoders) + if err != nil { + errs = append(errs, err) break } + objects[i] = decoded } } return errs @@ -84,16 +130,6 @@ type MultiObjectTyper []ObjectTyper var _ ObjectTyper = MultiObjectTyper{} -func (m MultiObjectTyper) DataKind(data []byte) (gvk unversioned.GroupVersionKind, err error) { - for _, t := range m { - gvk, err = t.DataKind(data) - if err == nil { - return - } - } - return -} - func (m MultiObjectTyper) ObjectKind(obj Object) (gvk unversioned.GroupVersionKind, err error) { for _, t := range m { gvk, err = t.ObjectKind(obj) @@ -122,3 +158,12 @@ func (m MultiObjectTyper) Recognizes(gvk unversioned.GroupVersionKind) bool { } return false } + +func (m MultiObjectTyper) IsUnversioned(obj Object) (bool, bool) { + for _, t := range m { + if unversioned, ok := t.IsUnversioned(obj); ok { + return unversioned, true + } + } + return false, false +} diff --git a/pkg/runtime/helper_test.go b/pkg/runtime/helper_test.go index 36434054cde..be7f0dedda1 100644 --- a/pkg/runtime/helper_test.go +++ b/pkg/runtime/helper_test.go @@ -32,7 +32,7 @@ func TestDecodeList(t *testing.T) { &runtime.Unstructured{TypeMeta: runtime.TypeMeta{Kind: "Foo", APIVersion: "Bar"}, Object: map[string]interface{}{"test": "value"}}, }, } - if errs := runtime.DecodeList(pl.Items, api.Scheme); len(errs) != 0 { + if errs := runtime.DecodeList(pl.Items, testapi.Default.Codec()); len(errs) != 0 { t.Fatalf("unexpected error %v", errs) } if pod, ok := pl.Items[1].(*api.Pod); !ok || pod.Name != "test" { diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go index 7e6eb647686..6a51ee05ec8 100644 --- a/pkg/runtime/interfaces.go +++ b/pkg/runtime/interfaces.go @@ -23,70 +23,84 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" ) -// Codec defines methods for serializing and deserializing API objects. -type Codec interface { - Decoder - Encoder +const ( + APIVersionInternal = "__internal" + APIVersionUnversioned = "__unversioned" +) + +// Typer retrieves information about an object's group, version, and kind. +type Typer interface { + // ObjectKind returns the version and kind of the provided object, or an + // error if the object is not recognized (IsNotRegisteredError will return true). + // It returns whether the object is considered unversioned at the same time. + // TODO: align the signature of ObjectTyper with this interface + ObjectKind(Object) (*unversioned.GroupVersionKind, bool, error) } -// Decoder defines methods for deserializing API objects into a given type -type Decoder interface { - // TODO: change the signature of this method - Decode(data []byte) (Object, error) - // DEPRECATED: This method is being removed - DecodeToVersion(data []byte, groupVersion unversioned.GroupVersion) (Object, error) - // DEPRECATED: This method is being removed - DecodeInto(data []byte, obj Object) error - // DEPRECATED: This method is being removed - DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, groupVersionKind unversioned.GroupVersionKind) error - - DecodeParametersInto(parameters url.Values, obj Object) error -} - -// Encoder defines methods for serializing API objects into bytes type Encoder interface { - // DEPRECATED: This method is being removed - Encode(obj Object) (data []byte, err error) - EncodeToStream(obj Object, stream io.Writer) error - - // TODO: Add method for processing url parameters. - // EncodeParameters(obj Object) (url.Values, error) + // EncodeToStream writes an object to a stream. Override versions may be provided for each group + // that enforce a certain versioning. Implementations may return errors if the versions are incompatible, + // or if no conversion is defined. + EncodeToStream(obj Object, stream io.Writer, overrides ...unversioned.GroupVersion) error } -// ObjectCodec represents the common mechanisms for converting to and from a particular -// binary representation of an object. -// TODO: Remove this interface - it is used only in CodecFor() method. -type ObjectCodec interface { - Decoder - - // EncodeToVersion convert and serializes an object in the internal format - // to a specified output version. An error is returned if the object - // cannot be converted for any reason. - EncodeToVersion(obj Object, outVersion string) ([]byte, error) - EncodeToVersionStream(obj Object, outVersion string, stream io.Writer) error +type Decoder interface { + // Decode attempts to deserialize the provided data using either the innate typing of the scheme or the + // default kind, group, and version provided. It returns a decoded object as well as the kind, group, and + // version from the serialized data, or an error. If into is non-nil, it will be used as the target type + // and implementations may choose to use it rather than reallocating an object. However, the object is not + // guaranteed to be populated. The returned object is not guaranteed to match into. If defaults are + // provided, they are applied to the data by default. If no defaults or partial defaults are provided, the + // type of the into may be used to guide conversion decisions. + Decode(data []byte, defaults *unversioned.GroupVersionKind, into Object) (Object, *unversioned.GroupVersionKind, error) } -// ObjectDecoder is a convenience interface for identifying serialized versions of objects -// and transforming them into Objects. It intentionally overlaps with ObjectTyper and -// Decoder for use in decode only paths. -// TODO: Consider removing this interface? -type ObjectDecoder interface { +// Serializer is the core interface for transforming objects into a serialized format and back. +// Implementations may choose to perform conversion of the object, but no assumptions should be made. +type Serializer interface { + Encoder Decoder - // DataVersionAndKind returns the group,version,kind of the provided data, or an error - // if another problem is detected. In many cases this method can be as expensive to - // invoke as the Decode method. - DataKind([]byte) (unversioned.GroupVersionKind, error) - // Recognizes returns true if the scheme is able to handle the provided group,version,kind - // of an object. - Recognizes(unversioned.GroupVersionKind) bool +} + +// Codec is a Serializer that deals with the details of versioning objects. It offers the same +// interface as Serializer, so this is a marker to consumers that care about the version of the objects +// they receive. +type Codec Serializer + +// ParameterCodec defines methods for serializing and deserializing API objects to url.Values and +// performing any necessary conversion. Unlike the normal Codec, query parameters are not self describing +// and the desired version must be specified. +type ParameterCodec interface { + // DecodeParameters takes the given url.Values in the specified group version and decodes them + // into the provided object, or returns an error. + DecodeParameters(parameters url.Values, from unversioned.GroupVersion, into Object) error + // EncodeParameters encodes the provided object as query parameters or returns an error. + EncodeParameters(obj Object, to unversioned.GroupVersion) (url.Values, error) +} + +// NegotiatedSerializer is an interface used for obtaining encoders, decoders, and serializers +// for multiple supported media types. +type NegotiatedSerializer interface { + SupportedMediaTypes() []string + SerializerForMediaType(mediaType string, options map[string]string) (Serializer, bool) + EncoderForVersion(serializer Serializer, gv unversioned.GroupVersion) Encoder + DecoderToVersion(serializer Serializer, gv unversioned.GroupVersion) Decoder } /////////////////////////////////////////////////////////////////////////////// // Non-codec interfaces +type ObjectVersioner interface { + ConvertToVersion(in Object, outVersion string) (out Object, err error) +} + // ObjectConvertor converts an object to a different version. type ObjectConvertor interface { + // Convert attempts to convert one object into another, or returns an error. This method does + // not guarantee the in object is not mutated. Convert(in, out interface{}) error + // ConvertToVersion takes the provided object and converts it the provided version. This + // method does not guarantee that the in object is not mutated. ConvertToVersion(in Object, outVersion string) (out Object, err error) ConvertFieldLabel(version, kind, label, value string) (string, string, error) } @@ -94,10 +108,6 @@ type ObjectConvertor interface { // ObjectTyper contains methods for extracting the APIVersion and Kind // of objects. type ObjectTyper interface { - // DataKind returns the group,version,kind of the provided data, or an error - // if another problem is detected. In many cases this method can be as expensive to - // invoke as the Decode method. - DataKind([]byte) (unversioned.GroupVersionKind, error) // ObjectKind returns the default group,version,kind of the provided object, or an // error if the object is not recognized (IsNotRegisteredError will return true). ObjectKind(Object) (unversioned.GroupVersionKind, error) @@ -108,6 +118,10 @@ type ObjectTyper interface { // or more precisely that the provided version is a possible conversion or decoding // target. Recognizes(gvk unversioned.GroupVersionKind) bool + // IsUnversioned returns true if the provided object is considered unversioned and thus + // should have Version and Group suppressed in the output. If the object is not recognized + // in the scheme, ok is false. + IsUnversioned(Object) (unversioned bool, ok bool) } // ObjectCreater contains methods for instantiating an object by kind and version. diff --git a/pkg/runtime/register.go b/pkg/runtime/register.go index 53186957ac0..95244913c10 100644 --- a/pkg/runtime/register.go +++ b/pkg/runtime/register.go @@ -20,16 +20,6 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" ) -// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed PluginBase -func (obj *PluginBase) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - _, obj.Kind = gvk.ToAPIVersionAndKind() -} - -// GroupVersionKind satisfies the ObjectKind interface for all objects that embed PluginBase -func (obj *PluginBase) GroupVersionKind() *unversioned.GroupVersionKind { - return unversioned.FromAPIVersionAndKind("", obj.Kind) -} - // SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta func (obj *TypeMeta) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind() @@ -42,3 +32,33 @@ func (obj *TypeMeta) GroupVersionKind() *unversioned.GroupVersionKind { func (obj *Unknown) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *Unstructured) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } + +// GetObjectKind implements Object for VersionedObjects, returning an empty ObjectKind +// interface if no objects are provided, or the ObjectKind interface of the object in the +// highest array position. +func (obj *VersionedObjects) GetObjectKind() unversioned.ObjectKind { + last := obj.Last() + if last == nil { + return unversioned.EmptyObjectKind + } + return last.GetObjectKind() +} + +// First returns the leftmost object in the VersionedObjects array, which is usually the +// object as serialized on the wire. +func (obj *VersionedObjects) First() Object { + if len(obj.Objects) == 0 { + return nil + } + return obj.Objects[0] +} + +// Last is the rightmost object in the VersionedObjects array, which is the object after +// all transformations have been applied. This is the same object that would be returned +// by Decode in a normal invocation (without VersionedObjects in the into argument). +func (obj *VersionedObjects) Last() Object { + if len(obj.Objects) == 0 { + return nil + } + return obj.Objects[len(obj.Objects)-1] +} diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 8b28d11a630..474a6eb307f 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -17,9 +17,7 @@ limitations under the License. package runtime import ( - "encoding/json" "fmt" - "io" "net/url" "reflect" @@ -36,9 +34,6 @@ type Scheme struct { fieldLabelConversionFuncs map[string]map[string]FieldLabelConversionFunc } -var _ Decoder = &Scheme{} -var _ ObjectTyper = &Scheme{} - // Function to convert a field selector to internal representation. type FieldLabelConversionFunc func(label, value string) (internalLabel, internalValue string, err error) @@ -55,205 +50,11 @@ func (self *Scheme) fromScope(s conversion.Scope) (inVersion, outVersion string, return inVersion, outVersion, scheme } -// emptyPlugin is used to copy the Kind field to and from plugin objects. -type emptyPlugin struct { - PluginBase `json:",inline"` -} - -// embeddedObjectToRawExtension does the conversion you would expect from the name, using the information -// given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins; -// see the comment for RawExtension. -func (self *Scheme) embeddedObjectToRawExtension(in *EmbeddedObject, out *RawExtension, s conversion.Scope) error { - if in.Object == nil { - out.RawJSON = []byte("null") - return nil - } - - // Figure out the type and kind of the output object. - _, outGroupVersionString, scheme := self.fromScope(s) - objKind, err := scheme.raw.ObjectKind(in.Object) - if err != nil { - return err - } - outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString) - if err != nil { - return err - } - - // Manufacture an object of this type and kind. - outObj, err := scheme.New(outVersion.WithKind(objKind.Kind)) - if err != nil { - return err - } - - // Manually do the conversion. - err = s.Convert(in.Object, outObj, 0) - if err != nil { - return err - } - - // Copy the kind field into the output object. - err = s.Convert( - &emptyPlugin{PluginBase: PluginBase{Kind: objKind.Kind}}, - outObj, - conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames, - ) - if err != nil { - return err - } - // Because we provide the correct version, EncodeToVersion will not attempt a conversion. - raw, err := scheme.EncodeToVersion(outObj, outVersion.String()) - if err != nil { - // TODO: if this fails, create an Unknown-- maybe some other - // component will understand it. - return err - } - out.RawJSON = raw - return nil -} - -// rawExtensionToEmbeddedObject does the conversion you would expect from the name, using the information -// given in conversion.Scope. It's placed in all schemes as a ConversionFunc to enable plugins; -// see the comment for RawExtension. -func (self *Scheme) rawExtensionToEmbeddedObject(in *RawExtension, out *EmbeddedObject, s conversion.Scope) error { - if len(in.RawJSON) == 0 || (len(in.RawJSON) == 4 && string(in.RawJSON) == "null") { - out.Object = nil - return nil - } - // Figure out the type and kind of the output object. - inGroupVersionString, outGroupVersionString, scheme := self.fromScope(s) - dataKind, err := scheme.raw.DataKind(in.RawJSON) - if err != nil { - return err - } - inVersion, err := unversioned.ParseGroupVersion(inGroupVersionString) - if err != nil { - return err - } - outVersion, err := unversioned.ParseGroupVersion(outGroupVersionString) - if err != nil { - return err - } - - // We have to make this object ourselves because we don't store the version field for - // plugin objects. - inObj, err := scheme.New(inVersion.WithKind(dataKind.Kind)) - if err != nil { - return err - } - - err = DecodeInto(scheme, in.RawJSON, inObj) - if err != nil { - return err - } - - // Make the desired internal version, and do the conversion. - outObj, err := scheme.New(outVersion.WithKind(dataKind.Kind)) - if err != nil { - return err - } - err = scheme.Convert(inObj, outObj) - if err != nil { - return err - } - // Last step, clear the Kind field; that should always be blank in memory. - err = s.Convert( - &emptyPlugin{PluginBase: PluginBase{Kind: ""}}, - outObj, - conversion.SourceToDest|conversion.IgnoreMissingFields|conversion.AllowDifferentFieldTypeNames, - ) - if err != nil { - return err - } - out.Object = outObj - return nil -} - -// runtimeObjectToRawExtensionArray takes a list of objects and encodes them as RawExtension in the output version -// defined by the conversion.Scope. If objects must be encoded to different schema versions than the default, you -// should encode them yourself with runtime.Unknown, or convert the object prior to invoking conversion. Objects -// outside of the current scheme must be added as runtime.Unknown. -func (self *Scheme) runtimeObjectToRawExtensionArray(in *[]Object, out *[]RawExtension, s conversion.Scope) error { - src := *in - dest := make([]RawExtension, len(src)) - - _, outVersion, scheme := self.fromScope(s) - - for i := range src { - switch t := src[i].(type) { - case *Unknown: - // TODO: this should be decoupled from the scheme (since it is JSON specific) - dest[i].RawJSON = t.RawJSON - case *Unstructured: - // TODO: this should be decoupled from the scheme (since it is JSON specific) - data, err := json.Marshal(t.Object) - if err != nil { - return err - } - dest[i].RawJSON = data - default: - version := outVersion - // if the object exists - // this code is try to set the outputVersion, but only if the object has a non-internal group version - if inGVK, err := scheme.ObjectKind(src[i]); err == nil && !inGVK.GroupVersion().IsEmpty() { - if self.raw.InternalVersions[inGVK.Group] != inGVK.GroupVersion() { - version = inGVK.GroupVersion().String() - } - } - data, err := scheme.EncodeToVersion(src[i], version) - if err != nil { - return err - } - dest[i].RawJSON = data - } - } - *out = dest - return nil -} - -// rawExtensionToRuntimeObjectArray attempts to decode objects from the array - if they are unrecognized objects, -// they are added as Unknown. -func (self *Scheme) rawExtensionToRuntimeObjectArray(in *[]RawExtension, out *[]Object, s conversion.Scope) error { - src := *in - dest := make([]Object, len(src)) - - _, _, scheme := self.fromScope(s) - - for i := range src { - data := src[i].RawJSON - dataKind, err := scheme.raw.DataKind(data) - if err != nil { - return err - } - dest[i] = &Unknown{ - TypeMeta: TypeMeta{ - APIVersion: dataKind.GroupVersion().String(), - Kind: dataKind.Kind, - }, - RawJSON: data, - } - } - *out = dest - return nil -} - // NewScheme creates a new Scheme. This scheme is pluggable by default. -func NewScheme(internalGroupVersions ...unversioned.GroupVersion) *Scheme { +func NewScheme() *Scheme { s := &Scheme{conversion.NewScheme(), map[string]map[string]FieldLabelConversionFunc{}} + s.AddConversionFuncs(DefaultEmbeddedConversions()...) - for _, internalGV := range internalGroupVersions { - s.raw.InternalVersions[internalGV.Group] = internalGV - } - - s.raw.MetaFactory = conversion.SimpleMetaFactory{BaseFields: []string{"TypeMeta"}, VersionField: "APIVersion", KindField: "Kind"} - if err := s.raw.AddConversionFuncs( - s.embeddedObjectToRawExtension, - s.rawExtensionToEmbeddedObject, - s.runtimeObjectToRawExtensionArray, - s.rawExtensionToRuntimeObjectArray, - ); err != nil { - panic(err) - } // Enable map[string][]string conversions by default if err := s.raw.AddConversionFuncs(DefaultStringConversions...); err != nil { panic(err) @@ -267,14 +68,22 @@ func NewScheme(internalGroupVersions ...unversioned.GroupVersion) *Scheme { return s } -// AddInternalGroupVersion registers an internal GroupVersion with the scheme. This can later be -// used to lookup the internal GroupVersion for a given Group -func (s *Scheme) AddInternalGroupVersion(gv unversioned.GroupVersion) { - s.raw.InternalVersions[gv.Group] = gv +// AddUnversionedTypes registers the provided types as "unversioned", which means that they follow special rules. +// Whenever an object of this type is serialized, it is serialized with the provided group version and is not +// converted. Thus unversioned objects are expected to remain backwards compatible forever, as if they were in an +// API group and version that would never be updated. +// +// TODO: there is discussion about removing unversioned and replacing it with objects that are manifest into +// every version with particular schemas. Resolve tihs method at that point. +func (s *Scheme) AddUnversionedTypes(gv unversioned.GroupVersion, types ...Object) { + interfaces := make([]interface{}, len(types)) + for i := range types { + interfaces[i] = types[i] + } + s.raw.AddUnversionedTypes(gv, interfaces...) } // AddKnownTypes registers the types of the arguments to the marshaller of the package api. -// Encode() refuses the object unless its type is registered with AddKnownTypes. func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { interfaces := make([]interface{}, len(types)) for i := range types { @@ -283,6 +92,12 @@ func (s *Scheme) AddKnownTypes(gv unversioned.GroupVersion, types ...Object) { s.raw.AddKnownTypes(gv, interfaces...) } +// AddIgnoredConversionType declares a particular conversion that should be ignored - during conversion +// this method is not invoked. +func (s *Scheme) AddIgnoredConversionType(from, to interface{}) error { + return s.raw.AddIgnoredConversionType(from, to) +} + // AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should // be encoded as. Useful for testing when you don't want to make multiple packages to define // your structs. @@ -296,12 +111,6 @@ func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type return s.raw.KnownTypes(gv) } -// DataKind will return the group,version,kind of the given wire-format -// encoding of an API Object, or an error. -func (s *Scheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { - return s.raw.DataKind(data) -} - // ObjectKind returns the default group,version,kind of the given Object. func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) { return s.raw.ObjectKind(obj) @@ -318,7 +127,12 @@ func (s *Scheme) Recognizes(gvk unversioned.GroupVersionKind) bool { return s.raw.Recognizes(gvk) } -// New returns a new API object of the given kind, or an error if it hasn't been registered. +func (s *Scheme) IsUnversioned(obj Object) (bool, bool) { + return s.raw.IsUnversioned(obj) +} + +// New returns a new API object of the given version ("" for internal +// representation) and name, or an error if it hasn't been registered. func (s *Scheme) New(kind unversioned.GroupVersionKind) (Object, error) { obj, err := s.raw.NewObject(kind) if err != nil { @@ -394,11 +208,31 @@ func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error { return s.raw.AddDefaultingFuncs(defaultingFuncs...) } +// Copy does a deep copy of an API object. +func (s *Scheme) Copy(src Object) (Object, error) { + dst, err := s.raw.DeepCopy(src) + if err != nil { + return nil, err + } + return dst.(Object), nil +} + // Performs a deep copy of the given object. func (s *Scheme) DeepCopy(src interface{}) (interface{}, error) { return s.raw.DeepCopy(src) } +// WithConversions returns an ObjectConvertor that has the additional conversion functions +// defined in fns. The current scheme is not altered. +func (s *Scheme) WithConversions(fns *conversion.ConversionFuncs) ObjectConvertor { + if fns == nil { + return s + } + copied := *s + copied.raw = s.raw.WithConversions(*fns) + return &copied +} + // Convert will attempt to convert in into out. Both must be pointers. // For easy testing of conversion functions. Returns an error if the conversion isn't // possible. @@ -423,8 +257,19 @@ func (s *Scheme) ConvertFieldLabel(version, kind, label, value string) (string, // version within this scheme. Will return an error if the provided version does not // contain the inKind (or a mapping by name defined with AddKnownTypeWithName). Will also // return an error if the conversion does not result in a valid Object being -// returned. +// returned. The serializer handles loading/serializing nested objects. func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error) { + gv, err := unversioned.ParseGroupVersion(outVersion) + if err != nil { + return nil, err + } + switch in.(type) { + case *Unknown, *Unstructured: + old := in.GetObjectKind().GroupVersionKind() + defer in.GetObjectKind().SetGroupVersionKind(old) + setTargetVersion(in, s.raw, gv) + return in, nil + } unknown, err := s.raw.ConvertToVersion(in, outVersion) if err != nil { return nil, err @@ -433,105 +278,16 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion string) (Object, error) if !ok { return nil, fmt.Errorf("the provided object cannot be converted to a runtime.Object: %#v", unknown) } + setTargetVersion(obj, s.raw, gv) return obj, nil } -// EncodeToVersion turns the given api object into an appropriate JSON string. -// Will return an error if the object doesn't have an embedded TypeMeta. -// Obj may be a pointer to a struct, or a struct. If a struct, a copy -// must be made. If a pointer, the object may be modified before encoding, -// but will be put back into its original state before returning. -// -// Memory/wire format differences: -// * Having to keep track of the Kind and APIVersion fields makes tests -// very annoying, so the rule is that they are set only in wire format -// (json), not when in native (memory) format. This is possible because -// both pieces of information are implicit in the go typed object. -// * An exception: note that, if there are embedded API objects of known -// type, for example, PodList{... Items []Pod ...}, these embedded -// objects must be of the same version of the object they are embedded -// within, and their APIVersion and Kind must both be empty. -// * Note that the exception does not apply to the APIObject type, which -// recursively does Encode()/Decode(), and is capable of expressing any -// API object. -// * Only versioned objects should be encoded. This means that, if you pass -// a native object, Encode will convert it to a versioned object. For -// example, an api.Pod will get converted to a v1.Pod. However, if -// you pass in an object that's already versioned (v1.Pod), Encode -// will not modify it. -// -// The purpose of the above complex conversion behavior is to allow us to -// change the memory format yet not break compatibility with any stored -// objects, whether they be in our storage layer (e.g., etcd), or in user's -// config files. -func (s *Scheme) EncodeToVersion(obj Object, destVersion string) (data []byte, err error) { - return s.raw.EncodeToVersion(obj, destVersion) -} - -func (s *Scheme) EncodeToVersionStream(obj Object, destVersion string, stream io.Writer) error { - return s.raw.EncodeToVersionStream(obj, destVersion, stream) -} - -// Decode converts a YAML or JSON string back into a pointer to an api object. -// Deduces the type based upon the APIVersion and Kind fields, which are set -// by Encode. Only versioned objects (APIVersion != "") are accepted. The object -// will be converted into the in-memory unversioned type before being returned. -func (s *Scheme) Decode(data []byte) (Object, error) { - obj, err := s.raw.Decode(data) - if err != nil { - return nil, err +func setTargetVersion(obj Object, raw *conversion.Scheme, gv unversioned.GroupVersion) { + if gv.Version == APIVersionInternal { + // internal is a special case + obj.GetObjectKind().SetGroupVersionKind(nil) + } else { + gvk, _ := raw.ObjectKind(obj) + obj.GetObjectKind().SetGroupVersionKind(&unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvk.Kind}) } - return obj.(Object), nil -} - -// DecodeToVersion converts a YAML or JSON string back into a pointer to an api -// object. Deduces the type based upon the APIVersion and Kind fields, which -// are set by Encode. Only versioned objects (APIVersion != "") are -// accepted. The object will be converted into the in-memory versioned type -// requested before being returned. -func (s *Scheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) { - obj, err := s.raw.DecodeToVersion(data, gv) - if err != nil { - return nil, err - } - return obj.(Object), nil -} - -// DecodeInto parses a YAML or JSON string and stores it in obj. Returns an error -// if data.Kind is set and doesn't match the type of obj. Obj should be a -// pointer to an api type. -// If obj's APIVersion doesn't match that in data, an attempt will be made to convert -// data into obj's version. -// TODO: allow Decode/DecodeInto to take a default apiVersion and a default kind, to -// be applied if the provided object does not have either field (integrate external -// apis into the decoding scheme). -func (s *Scheme) DecodeInto(data []byte, obj Object) error { - return s.raw.DecodeInto(data, obj) -} - -// DecodeIntoWithSpecifiedVersionKind coerces the data into the obj, assuming that the data is -// of type GroupVersionKind -func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error { - return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, gvk) -} - -func (s *Scheme) DecodeParametersInto(parameters url.Values, obj Object) error { - return s.raw.DecodeParametersInto(parameters, obj) -} - -// Copy does a deep copy of an API object. Useful mostly for tests. -func (s *Scheme) Copy(src Object) (Object, error) { - dst, err := s.raw.DeepCopy(src) - if err != nil { - return nil, err - } - return dst.(Object), nil -} - -func (s *Scheme) CopyOrDie(obj Object) Object { - newObj, err := s.Copy(obj) - if err != nil { - panic(err) - } - return newObj } diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index f406585f970..d0f9f2d08bf 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -23,6 +23,8 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" + "k8s.io/kubernetes/pkg/util" ) type TypeMeta struct { @@ -51,23 +53,19 @@ type ExternalSimple struct { } func (obj *InternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *InternalSimple) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} func (obj *ExternalSimple) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalSimple) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} func TestScheme(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) + // If set, would clear TypeMeta during conversion. + //scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{}) + // test that scheme is an ObjectTyper var _ runtime.ObjectTyper = scheme @@ -102,21 +100,27 @@ func TestScheme(t *testing.T) { }, ) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Fatalf("unexpected error: %v", err) } + + codecs := serializer.NewCodecFactory(scheme) + codec := codecs.LegacyCodec(externalGV) + jsonserializer, _ := codecs.SerializerForFileExtension("json") + simple := &InternalSimple{ TestString: "foo", } // Test Encode, Decode, DecodeInto, and DecodeToVersion obj := runtime.Object(simple) - data, err := scheme.EncodeToVersion(obj, externalGV.String()) - obj2, err2 := runtime.Decode(scheme, data) - obj3 := &InternalSimple{} - err3 := runtime.DecodeInto(scheme, data, obj3) - obj4, err4 := scheme.DecodeToVersion(data, externalGV) - if err != nil || err2 != nil || err3 != nil || err4 != nil { - t.Fatalf("Failure: '%v' '%v' '%v' '%v'", err, err2, err3, err4) + data, err := runtime.Encode(codec, obj) + if err != nil { + t.Fatal(err) + } + + obj2, err := runtime.Decode(codec, data) + if err != nil { + t.Fatal(err) } if _, ok := obj2.(*InternalSimple); !ok { t.Fatalf("Got wrong type") @@ -124,9 +128,22 @@ func TestScheme(t *testing.T) { if e, a := simple, obj2; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } + + obj3 := &InternalSimple{} + if err := runtime.DecodeInto(codec, data, obj3); err != nil { + t.Fatal(err) + } + // clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion + // does not automatically clear TypeMeta anymore). + simple.TypeMeta = TypeMeta{Kind: "Simple", APIVersion: externalGV.String()} if e, a := simple, obj3; !reflect.DeepEqual(e, a) { t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a) } + + obj4, err := runtime.Decode(jsonserializer, data) + if err != nil { + t.Fatal(err) + } if _, ok := obj4.(*ExternalSimple); !ok { t.Fatalf("Got wrong type") } @@ -135,7 +152,7 @@ func TestScheme(t *testing.T) { external := &ExternalSimple{} err = scheme.Convert(simple, external) if err != nil { - t.Errorf("Unexpected error: %v", err) + t.Fatalf("Unexpected error: %v", err) } if e, a := simple.TestString, external.TestString; e != a { t.Errorf("Expected %v, got %v", e, a) @@ -145,36 +162,23 @@ func TestScheme(t *testing.T) { if e, a := 2, internalToExternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } - // Decode and DecodeInto should each have caused an increment. + // DecodeInto and Decode should each have caused an increment because of a conversion if e, a := 2, externalToInternalCalls; e != a { t.Errorf("Expected %v, got %v", e, a) } } -func TestInvalidObjectValueKind(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "", Version: ""} - - scheme := runtime.NewScheme() - scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) - - embedded := &runtime.EmbeddedObject{} - switch obj := embedded.Object.(type) { - default: - _, err := scheme.ObjectKind(obj) - if err == nil { - t.Errorf("Expected error on invalid kind") - } - } -} - func TestBadJSONRejection(t *testing.T) { scheme := runtime.NewScheme() + codecs := serializer.NewCodecFactory(scheme) + jsonserializer, _ := codecs.SerializerForFileExtension("json") + badJSONMissingKind := []byte(`{ }`) - if _, err := runtime.Decode(scheme, badJSONMissingKind); err == nil { + if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil { t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind) } badJSONUnknownType := []byte(`{"kind": "bar"}`) - if _, err1 := runtime.Decode(scheme, badJSONUnknownType); err1 == nil { + if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil { t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType) } /*badJSONKindMismatch := []byte(`{"kind": "Pod"}`) @@ -184,13 +188,13 @@ func TestBadJSONRejection(t *testing.T) { } type ExtensionA struct { - runtime.PluginBase `json:",inline"` - TestString string `json:"testString"` + TypeMeta `json:",inline"` + TestString string `json:"testString"` } type ExtensionB struct { - runtime.PluginBase `json:",inline"` - TestString string `json:"testString"` + TypeMeta `json:",inline"` + TestString string `json:"testString"` } type ExternalExtensionType struct { @@ -200,7 +204,7 @@ type ExternalExtensionType struct { type InternalExtensionType struct { TypeMeta `json:",inline"` - Extension runtime.EmbeddedObject `json:"extension"` + Extension runtime.Object `json:"extension"` } type ExternalOptionalExtensionType struct { @@ -210,143 +214,135 @@ type ExternalOptionalExtensionType struct { type InternalOptionalExtensionType struct { TypeMeta `json:",inline"` - Extension runtime.EmbeddedObject `json:"extension,omitempty"` + Extension runtime.Object `json:"extension,omitempty"` } -func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.PluginBase } -func (obj *ExtensionA) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - _, obj.PluginBase.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.PluginBase } -func (obj *ExtensionB) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - _, obj.PluginBase.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} -func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *InternalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} +func (obj *ExtensionA) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *ExtensionB) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *ExternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } +func (obj *InternalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } func (obj *ExternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *ExternalOptionalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} func (obj *InternalOptionalExtensionType) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta } -func (obj *InternalOptionalExtensionType) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { - obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind() -} func TestExternalToInternalMapping(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) + codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) + table := []struct { obj runtime.Object encoded string }{ { - &InternalOptionalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}}, + &InternalOptionalExtensionType{Extension: nil}, `{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`, }, } - for _, item := range table { - gotDecoded, err := runtime.Decode(scheme, []byte(item.encoded)) + for i, item := range table { + gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { - var eEx, aEx runtime.Object - if obj, ok := e.(*InternalOptionalExtensionType); ok { - eEx = obj.Extension.Object - } - if obj, ok := a.(*InternalOptionalExtensionType); ok { - aEx = obj.Extension.Object - } - t.Errorf("expected %#v, got %#v (%#v, %#v)", e, a, eEx, aEx) + t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a)) } } } func TestExtensionMapping(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{}) scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{}) - scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{}) - scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{}) scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{}) scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{}) + + // register external first when the object is the same in both schemes, so ObjectVersionAndKind reports the + // external version. scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{}) scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{}) + scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{}) + scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{}) + + codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) table := []struct { - obj runtime.Object - encoded string + obj runtime.Object + expected runtime.Object + encoded string }{ { - &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionA{TestString: "foo"}}}, - `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"A","testString":"foo"}} + &InternalExtensionType{ + Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}), + }, + &InternalExtensionType{ + Extension: &runtime.Unknown{ + RawJSON: []byte(`{"kind":"A","apiVersion":"test.group/testExternal","testString":"foo"}`), + }, + }, + // apiVersion is set in the serialized object for easier consumption by clients + `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"A","apiVersion":"test.group/testExternal","testString":"foo"}} `, }, { - &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: &ExtensionB{TestString: "bar"}}}, - `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"B","testString":"bar"}} + &InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})}, + &InternalExtensionType{ + Extension: &runtime.Unknown{ + RawJSON: []byte(`{"kind":"B","apiVersion":"test.group/testExternal","testString":"bar"}`), + }, + }, + // apiVersion is set in the serialized object for easier consumption by clients + `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":{"kind":"B","apiVersion":"test.group/testExternal","testString":"bar"}} `, }, { - &InternalExtensionType{Extension: runtime.EmbeddedObject{Object: nil}}, + &InternalExtensionType{Extension: nil}, + &InternalExtensionType{ + Extension: nil, + }, `{"kind":"ExtensionType","apiVersion":"` + externalGV.String() + `","extension":null} `, }, } - for _, item := range table { - gotEncoded, err := scheme.EncodeToVersion(item.obj, externalGV.String()) + for i, item := range table { + gotEncoded, err := runtime.Encode(codec, item.obj) if err != nil { t.Errorf("unexpected error '%v' (%#v)", err, item.obj) } else if e, a := item.encoded, string(gotEncoded); e != a { t.Errorf("expected\n%#v\ngot\n%#v\n", e, a) } - gotDecoded, err := runtime.Decode(scheme, []byte(item.encoded)) + gotDecoded, err := runtime.Decode(codec, []byte(item.encoded)) if err != nil { t.Errorf("unexpected error '%v' (%v)", err, item.encoded) - } else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) { - var eEx, aEx runtime.Object - if obj, ok := e.(*InternalExtensionType); ok { - eEx = obj.Extension.Object - } - if obj, ok := a.(*InternalExtensionType); ok { - aEx = obj.Extension.Object - } - t.Errorf("expected %#v, got %#v (%#v, %#v)", e, a, eEx, aEx) + } else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) { + t.Errorf("%d: unexpected objects:\n%s", i, util.ObjectGoPrintSideBySide(e, a)) } } } func TestEncode(t *testing.T) { - internalGV := unversioned.GroupVersion{Group: "test.group", Version: ""} + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} scheme := runtime.NewScheme() - scheme.AddInternalGroupVersion(internalGV) scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) - codec := runtime.CodecFor(scheme, externalGV) + + codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) + test := &InternalSimple{ TestString: "I'm the same", } obj := runtime.Object(test) data, err := runtime.Encode(codec, obj) - obj2, err2 := runtime.Decode(codec, data) + obj2, gvk, err2 := codec.Decode(data, nil, nil) if err != nil || err2 != nil { t.Fatalf("Failure: '%v' '%v'", err, err2) } @@ -354,6 +350,68 @@ func TestEncode(t *testing.T) { t.Fatalf("Got wrong type") } if !reflect.DeepEqual(obj2, test) { - t.Errorf("Expected:\n %#v,\n Got:\n %#v", &test, obj2) + t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) + } + if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) { + t.Errorf("unexpected gvk returned by decode: %#v", gvk) + } +} + +func TestUnversionedTypes(t *testing.T) { + internalGV := unversioned.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal} + externalGV := unversioned.GroupVersion{Group: "test.group", Version: "testExternal"} + otherGV := unversioned.GroupVersion{Group: "group", Version: "other"} + + scheme := runtime.NewScheme() + scheme.AddUnversionedTypes(externalGV, &InternalSimple{}) + scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{}) + scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{}) + scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{}) + + codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV) + + if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok { + t.Fatal("type not unversioned and in scheme: %t %t", unv, ok) + } + + kind, err := scheme.ObjectKind(&InternalSimple{}) + if err != nil { + t.Fatal(err) + } + if kind != externalGV.WithKind("InternalSimple") { + t.Fatalf("unexpected: %#v", kind) + } + + test := &InternalSimple{ + TestString: "I'm the same", + } + obj := runtime.Object(test) + data, err := runtime.Encode(codec, obj) + if err != nil { + t.Fatal(err) + } + obj2, gvk, err := codec.Decode(data, nil, nil) + if err != nil { + t.Fatal(err) + } + if _, ok := obj2.(*InternalSimple); !ok { + t.Fatalf("Got wrong type") + } + if !reflect.DeepEqual(obj2, test) { + t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2) + } + // object is serialized as an unversioned object (in the group and version it was defined in) + if !reflect.DeepEqual(gvk, &unversioned.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) { + t.Errorf("unexpected gvk returned by decode: %#v", gvk) + } + + // when serialized to a different group, the object is kept in its preferred name + codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV) + data, err = runtime.Encode(codec, obj) + if err != nil { + t.Fatal(err) + } + if string(data) != `{"kind":"InternalSimple","apiVersion":"test.group/testExternal","testString":"I'm the same"}`+"\n" { + t.Errorf("unexpected data: %s", data) } } diff --git a/pkg/runtime/serializer/codec_factory.go b/pkg/runtime/serializer/codec_factory.go new file mode 100644 index 00000000000..6f6310d9c83 --- /dev/null +++ b/pkg/runtime/serializer/codec_factory.go @@ -0,0 +1,176 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package serializer + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" + "k8s.io/kubernetes/pkg/runtime/serializer/recognizer" + "k8s.io/kubernetes/pkg/runtime/serializer/versioning" +) + +type serializerType struct { + AcceptContentTypes []string + ContentType string + FileExtensions []string + Serializer runtime.Serializer + PrettySerializer runtime.Serializer +} + +// NewCodecFactory provides methods for retrieving serializers for the supported wire formats +// and conversion wrappers to define preferred internal and external versions. In the future, +// as the internal version is used less, callers may instead use a defaulting serializer and +// only convert objects which are shared internally (Status, common API machinery). +// TODO: allow other codecs to be compiled in? +// TODO: accept a scheme interface +func NewCodecFactory(scheme *runtime.Scheme) CodecFactory { + return newCodecFactory(scheme, json.DefaultMetaFactory) +} + +// newCodecFactory is a helper for testing that allows a different metafactory to be specified. +func newCodecFactory(scheme *runtime.Scheme, mf json.MetaFactory) CodecFactory { + jsonSerializer := json.NewSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme), false) + jsonPrettySerializer := json.NewSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme), true) + yamlSerializer := json.NewYAMLSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme)) + serializers := []serializerType{ + { + AcceptContentTypes: []string{"application/json"}, + ContentType: "application/json", + FileExtensions: []string{"json"}, + Serializer: jsonSerializer, + PrettySerializer: jsonPrettySerializer, + }, + { + AcceptContentTypes: []string{"application/yaml"}, + ContentType: "application/yaml", + FileExtensions: []string{"yaml"}, + Serializer: yamlSerializer, + }, + } + decoders := make([]runtime.Decoder, 0, len(serializers)) + accepts := []string{} + alreadyAccepted := make(map[string]struct{}) + for _, d := range serializers { + decoders = append(decoders, d.Serializer) + for _, mediaType := range d.AcceptContentTypes { + if _, ok := alreadyAccepted[mediaType]; ok { + continue + } + alreadyAccepted[mediaType] = struct{}{} + accepts = append(accepts, mediaType) + } + } + return CodecFactory{ + scheme: scheme, + serializers: serializers, + universal: recognizer.NewDecoder(decoders...), + accepts: accepts, + + legacySerializer: jsonSerializer, + } +} + +// CodecFactory provides methods for retrieving codecs and serializers for specific +// versions and content types. +type CodecFactory struct { + scheme *runtime.Scheme + serializers []serializerType + universal runtime.Decoder + accepts []string + + legacySerializer runtime.Serializer +} + +var _ runtime.NegotiatedSerializer = &CodecFactory{} + +// SupportedMediaTypes returns the RFC2046 media types that this factory has serializers for. +func (f CodecFactory) SupportedMediaTypes() []string { + return f.accepts +} + +// LegacyCodec encodes output to a given API version, and decodes output into the internal form from +// any recognized source. The returned codec will always encode output to JSON. +// +// This method is deprecated - clients and servers should negotiate a serializer by mime-type and +// invoke CodecForVersions. Callers that need only to read data should use UniversalDecoder(). +func (f CodecFactory) LegacyCodec(version ...unversioned.GroupVersion) runtime.Codec { + return f.CodecForVersions(runtime.NewCodec(f.legacySerializer, f.universal), version, nil) +} + +// UniversalDeserializer can convert any stored data recognized by this factory into a Go object that satisfies +// runtime.Object. It does not perform conversion. It does not perform defaulting. +func (f CodecFactory) UniversalDeserializer() runtime.Decoder { + return f.universal +} + +// UniversalDecoder returns a runtime.Decoder capable of decoding all known API objects in all known formats. Used +// by clients that do not need to encode objects but want to deserialize API objects stored on disk. Only decodes +// objects in groups registered with the scheme. The GroupVersions passed may be used to select alternate +// versions of objects to return - by default, runtime.APIVersionInternal is used. If any versions are specified, +// unrecognized groups will be returned in the version they are encoded as (no conversion). This decoder performs +// defaulting. +// +// TODO: the decoder will eventually be removed in favor of dealing with objects in their versioned form +func (f CodecFactory) UniversalDecoder(versions ...unversioned.GroupVersion) runtime.Decoder { + return f.CodecForVersions(runtime.NoopEncoder{f.universal}, nil, versions) +} + +// CodecFor creates a codec with the provided serializer. If an object is decoded and its group is not in the list, +// it will default to runtime.APIVersionInternal. If encode is not specified for an object's group, the object is not +// converted. If encode or decode are nil, no conversion is performed. +func (f CodecFactory) CodecForVersions(serializer runtime.Serializer, encode []unversioned.GroupVersion, decode []unversioned.GroupVersion) runtime.Codec { + return versioning.NewCodecForScheme(f.scheme, serializer, encode, decode) +} + +// DecoderToVersion returns a decoder that targets the provided group version. +func (f CodecFactory) DecoderToVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Decoder { + return f.CodecForVersions(serializer, nil, []unversioned.GroupVersion{gv}) +} + +// EncoderForVersion returns an encoder that targets the provided group version. +func (f CodecFactory) EncoderForVersion(serializer runtime.Serializer, gv unversioned.GroupVersion) runtime.Encoder { + return f.CodecForVersions(serializer, []unversioned.GroupVersion{gv}, nil) +} + +// SerializerForMediaType returns a serializer that matches the provided RFC2046 mediaType, or false if no such +// serializer exists +func (f CodecFactory) SerializerForMediaType(mediaType string, options map[string]string) (runtime.Serializer, bool) { + for _, s := range f.serializers { + for _, accepted := range s.AcceptContentTypes { + if accepted == mediaType { + if v, ok := options["pretty"]; ok && v == "1" && s.PrettySerializer != nil { + return s.PrettySerializer, true + } + return s.Serializer, true + } + } + } + return nil, false +} + +// SerializerForFileExtension returns a serializer for the provided extension, or false if no serializer matches. +func (f CodecFactory) SerializerForFileExtension(extension string) (runtime.Serializer, bool) { + for _, s := range f.serializers { + for _, ext := range s.FileExtensions { + if extension == ext { + return s.Serializer, true + } + } + } + return nil, false +} diff --git a/pkg/runtime/serializer/codec_test.go b/pkg/runtime/serializer/codec_test.go new file mode 100644 index 00000000000..6c1b2ff9547 --- /dev/null +++ b/pkg/runtime/serializer/codec_test.go @@ -0,0 +1,400 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package serializer + +import ( + "encoding/json" + "fmt" + "log" + "os" + "reflect" + "strings" + "testing" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/conversion" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util" + + "github.com/ghodss/yaml" + "github.com/google/gofuzz" + flag "github.com/spf13/pflag" +) + +var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.") + +type testMetaFactory struct{} + +func (testMetaFactory) Interpret(data []byte) (*unversioned.GroupVersionKind, error) { + findKind := struct { + APIVersion string `json:"myVersionKey,omitempty"` + ObjectKind string `json:"myKindKey,omitempty"` + }{} + // yaml is a superset of json, so we use it to decode here. That way, + // we understand both. + if err := yaml.Unmarshal(data, &findKind); err != nil { + return nil, fmt.Errorf("couldn't get version/kind: %v", err) + } + gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) + if err != nil { + return nil, err + } + return &unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: findKind.ObjectKind}, nil +} + +// Test a weird version/kind embedding format. +type MyWeirdCustomEmbeddedVersionKindField struct { + ID string `json:"ID,omitempty"` + APIVersion string `json:"myVersionKey,omitempty"` + ObjectKind string `json:"myKindKey,omitempty"` + Z string `json:"Z,omitempty"` + Y uint64 `json:"Y,omitempty"` +} + +type TestType1 struct { + MyWeirdCustomEmbeddedVersionKindField `json:",inline"` + A string `json:"A,omitempty"` + B int `json:"B,omitempty"` + C int8 `json:"C,omitempty"` + D int16 `json:"D,omitempty"` + E int32 `json:"E,omitempty"` + F int64 `json:"F,omitempty"` + G uint `json:"G,omitempty"` + H uint8 `json:"H,omitempty"` + I uint16 `json:"I,omitempty"` + J uint32 `json:"J,omitempty"` + K uint64 `json:"K,omitempty"` + L bool `json:"L,omitempty"` + M map[string]int `json:"M,omitempty"` + N map[string]TestType2 `json:"N,omitempty"` + O *TestType2 `json:"O,omitempty"` + P []TestType2 `json:"Q,omitempty"` +} + +type TestType2 struct { + A string `json:"A,omitempty"` + B int `json:"B,omitempty"` +} + +type ExternalTestType2 struct { + A string `json:"A,omitempty"` + B int `json:"B,omitempty"` +} +type ExternalTestType1 struct { + MyWeirdCustomEmbeddedVersionKindField `json:",inline"` + A string `json:"A,omitempty"` + B int `json:"B,omitempty"` + C int8 `json:"C,omitempty"` + D int16 `json:"D,omitempty"` + E int32 `json:"E,omitempty"` + F int64 `json:"F,omitempty"` + G uint `json:"G,omitempty"` + H uint8 `json:"H,omitempty"` + I uint16 `json:"I,omitempty"` + J uint32 `json:"J,omitempty"` + K uint64 `json:"K,omitempty"` + L bool `json:"L,omitempty"` + M map[string]int `json:"M,omitempty"` + N map[string]ExternalTestType2 `json:"N,omitempty"` + O *ExternalTestType2 `json:"O,omitempty"` + P []ExternalTestType2 `json:"Q,omitempty"` +} + +type ExternalInternalSame struct { + MyWeirdCustomEmbeddedVersionKindField `json:",inline"` + A TestType2 `json:"A,omitempty"` +} + +// TestObjectFuzzer can randomly populate all the above objects. +var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs( + func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) { + c.FuzzNoCustom(j) + j.APIVersion = "" + j.ObjectKind = "" + }, +) + +func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() unversioned.ObjectKind { return obj } +func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { + obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind() +} +func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() *unversioned.GroupVersionKind { + return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind) +} + +func (obj *ExternalInternalSame) GetObjectKind() unversioned.ObjectKind { + return &obj.MyWeirdCustomEmbeddedVersionKindField +} + +func (obj *TestType1) GetObjectKind() unversioned.ObjectKind { + return &obj.MyWeirdCustomEmbeddedVersionKindField +} + +func (obj *ExternalTestType1) GetObjectKind() unversioned.ObjectKind { + return &obj.MyWeirdCustomEmbeddedVersionKindField +} + +func (obj *TestType2) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } +func (obj *ExternalTestType2) GetObjectKind() unversioned.ObjectKind { + return unversioned.EmptyObjectKind +} + +// Returns a new Scheme set up with the test objects. +func GetTestScheme() (*runtime.Scheme, runtime.Codec) { + internalGV := unversioned.GroupVersion{Version: runtime.APIVersionInternal} + externalGV := unversioned.GroupVersion{Version: "v1"} + externalGV2 := unversioned.GroupVersion{Version: "v2"} + + s := runtime.NewScheme() + // Ordinarily, we wouldn't add TestType2, but because this is a test and + // both types are from the same package, we need to get it into the system + // so that converter will match it with ExternalType2. + s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{}) + s.AddKnownTypes(externalGV, &ExternalInternalSame{}) + s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) + s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{}) + s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{}) + s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{}) + s.AddKnownTypeWithName(externalGV2.WithKind("TestType1"), &ExternalTestType1{}) + + s.AddUnversionedTypes(externalGV, &unversioned.Status{}) + + cf := newCodecFactory(s, testMetaFactory{}) + codec := cf.LegacyCodec(unversioned.GroupVersion{Version: "v1"}) + return s, codec +} + +func objDiff(a, b interface{}) string { + ab, err := json.Marshal(a) + if err != nil { + panic("a") + } + bb, err := json.Marshal(b) + if err != nil { + panic("b") + } + return util.StringDiff(string(ab), string(bb)) + + // An alternate diff attempt, in case json isn't showing you + // the difference. (reflect.DeepEqual makes a distinction between + // nil and empty slices, for example.) + //return util.StringDiff( + // fmt.Sprintf("%#v", a), + // fmt.Sprintf("%#v", b), + //) +} + +var semantic = conversion.EqualitiesOrDie( + func(a, b MyWeirdCustomEmbeddedVersionKindField) bool { + a.APIVersion, a.ObjectKind = "", "" + b.APIVersion, b.ObjectKind = "", "" + return a == b + }, +) + +func runTest(t *testing.T, source interface{}) { + name := reflect.TypeOf(source).Elem().Name() + TestObjectFuzzer.Fuzz(source) + + _, codec := GetTestScheme() + data, err := runtime.Encode(codec, source.(runtime.Object)) + if err != nil { + t.Errorf("%v: %v (%#v)", name, err, source) + return + } + obj2, err := runtime.Decode(codec, data) + if err != nil { + t.Errorf("%v: %v (%v)", name, err, string(data)) + return + } + if !semantic.DeepEqual(source, obj2) { + t.Errorf("1: %v: diff: %v", name, util.ObjectGoPrintSideBySide(source, obj2)) + return + } + obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface() + if err := runtime.DecodeInto(codec, data, obj3.(runtime.Object)); err != nil { + t.Errorf("2: %v: %v", name, err) + return + } + if !semantic.DeepEqual(source, obj3) { + t.Errorf("3: %v: diff: %v", name, objDiff(source, obj3)) + return + } +} + +func TestTypes(t *testing.T) { + table := []interface{}{ + &TestType1{}, + &ExternalInternalSame{}, + } + for _, item := range table { + // Try a few times, since runTest uses random values. + for i := 0; i < *fuzzIters; i++ { + runTest(t, item) + } + } +} + +func TestVersionedEncoding(t *testing.T) { + s, codec := GetTestScheme() + out, err := runtime.Encode(codec, &TestType1{}, unversioned.GroupVersion{Version: "v2"}) + if err != nil { + t.Fatal(err) + } + if string(out) != `{"myVersionKey":"v2","myKindKey":"TestType1"}`+"\n" { + t.Fatal(string(out)) + } + _, err = runtime.Encode(codec, &TestType1{}, unversioned.GroupVersion{Version: "v3"}) + if err == nil { + t.Fatal(err) + } + + cf := newCodecFactory(s, testMetaFactory{}) + encoder, _ := cf.SerializerForFileExtension("json") + + // codec that is unversioned uses the target version + unversionedCodec := cf.CodecForVersions(encoder, nil, nil) + _, err = runtime.Encode(unversionedCodec, &TestType1{}, unversioned.GroupVersion{Version: "v3"}) + if err == nil || !runtime.IsNotRegisteredError(err) { + t.Fatal(err) + } + + // unversioned encode with no versions is written directly to wire + out, err = runtime.Encode(unversionedCodec, &TestType1{}) + if err != nil { + t.Fatal(err) + } + if string(out) != `{"myVersionKey":"__internal","myKindKey":"TestType1"}`+"\n" { + t.Fatal(string(out)) + } +} + +func TestMultipleNames(t *testing.T) { + _, codec := GetTestScheme() + + obj, _, err := codec.Decode([]byte(`{"myKindKey":"TestType3","myVersionKey":"v1","A":"value"}`), nil, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + internal := obj.(*TestType1) + if internal.A != "value" { + t.Fatalf("unexpected decoded object: %#v", internal) + } + + out, err := runtime.Encode(codec, internal) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !strings.Contains(string(out), `"myKindKey":"TestType1"`) { + t.Errorf("unexpected encoded output: %s", string(out)) + } +} + +func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) { + internalGV := unversioned.GroupVersion{Version: runtime.APIVersionInternal} + externalGV := unversioned.GroupVersion{Version: "v1"} + + s := runtime.NewScheme() + // create two names internally, with TestType1 being preferred + s.AddKnownTypeWithName(internalGV.WithKind("TestType1"), &TestType1{}) + s.AddKnownTypeWithName(internalGV.WithKind("OtherType1"), &TestType1{}) + // create two names externally, with TestType1 being preferred + s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{}) + s.AddKnownTypeWithName(externalGV.WithKind("OtherType1"), &ExternalTestType1{}) + + ext := &ExternalTestType1{} + ext.APIVersion = "v1" + ext.ObjectKind = "OtherType1" + ext.A = "test" + data, err := json.Marshal(ext) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + expect := &TestType1{A: "test"} + + codec := newCodecFactory(s, testMetaFactory{}).LegacyCodec(unversioned.GroupVersion{Version: "v1"}) + + obj, err := runtime.Decode(codec, data) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !semantic.DeepEqual(expect, obj) { + t.Errorf("unexpected object: %#v", obj) + } + + into := &TestType1{} + if err := runtime.DecodeInto(codec, data, into); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !semantic.DeepEqual(expect, into) { + t.Errorf("unexpected object: %#v", obj) + } +} + +func TestEncode_Ptr(t *testing.T) { + _, codec := GetTestScheme() + tt := &TestType1{A: "I am a pointer object"} + data, err := runtime.Encode(codec, tt) + obj2, err2 := runtime.Decode(codec, data) + if err != nil || err2 != nil { + t.Fatalf("Failure: '%v' '%v'\n%s", err, err2, data) + } + if _, ok := obj2.(*TestType1); !ok { + t.Fatalf("Got wrong type") + } + if !semantic.DeepEqual(obj2, tt) { + t.Errorf("Expected:\n %#v,\n Got:\n %#v", tt, obj2) + } +} + +func TestBadJSONRejection(t *testing.T) { + log.SetOutput(os.Stderr) + _, codec := GetTestScheme() + badJSONs := [][]byte{ + []byte(`{"myVersionKey":"v1"}`), // Missing kind + []byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind + []byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version + []byte(`{"myKindKey":"TestType1"}`), // Missing version + } + for _, b := range badJSONs { + if _, err := runtime.Decode(codec, b); err == nil { + t.Errorf("Did not reject bad json: %s", string(b)) + } + } + badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`) + if err := runtime.DecodeInto(codec, badJSONKindMismatch, &TestType1{}); err == nil { + t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch) + } + if err := runtime.DecodeInto(codec, []byte(``), &TestType1{}); err != nil { + t.Errorf("Should allow empty decode: %v", err) + } + if _, _, err := codec.Decode([]byte(``), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err == nil { + t.Errorf("Did not give error for empty data with only kind default") + } + if _, _, err := codec.Decode([]byte(`{"myVersionKey":"v1"}`), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err != nil { + t.Errorf("Gave error for version and kind default") + } + if _, _, err := codec.Decode([]byte(`{"myKindKey":"ExternalInternalSame"}`), &unversioned.GroupVersionKind{Version: "v1"}, nil); err != nil { + t.Errorf("Gave error for version and kind default") + } + if _, _, err := codec.Decode([]byte(``), &unversioned.GroupVersionKind{Kind: "ExternalInternalSame", Version: "v1"}, nil); err != nil { + t.Errorf("Gave error for version and kind defaulted: %v", err) + } + if _, err := runtime.Decode(codec, []byte(``)); err == nil { + t.Errorf("Did not give error for empty data") + } +} diff --git a/pkg/runtime/serializer/json/json.go b/pkg/runtime/serializer/json/json.go new file mode 100644 index 00000000000..f9fb4bbfb22 --- /dev/null +++ b/pkg/runtime/serializer/json/json.go @@ -0,0 +1,191 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package json + +import ( + "encoding/json" + "io" + + "github.com/ghodss/yaml" + "github.com/ugorji/go/codec" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + utilyaml "k8s.io/kubernetes/pkg/util/yaml" +) + +// NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer +// is not nil, the object has the group, version, and kind fields set. +func NewSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.Typer, pretty bool) runtime.Serializer { + return &Serializer{ + meta: meta, + creater: creater, + typer: typer, + yaml: false, + pretty: pretty, + } +} + +// NewYAMLSerializer creates a YAML serializer that handles encoding versioned objects into the proper YAML form. If typer +// is not nil, the object has the group, version, and kind fields set. This serializer supports only the subset of YAML that +// matches JSON, and will error if constructs are used that do not serialize to JSON. +func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.Typer) runtime.Serializer { + return &Serializer{ + meta: meta, + creater: creater, + typer: typer, + yaml: true, + } +} + +type Serializer struct { + meta MetaFactory + creater runtime.ObjectCreater + typer runtime.Typer + yaml bool + pretty bool +} + +// Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then +// load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown, the raw data will be +// extracted and no decoding will be performed. If into is not registered with the typer, then the object will be straight decoded using +// normal JSON/YAML unmarshalling. If into is provided and the original data is not fully qualified with kind/version/group, the type of +// the into will be used to alter the returned gvk. On success or most errors, the method will return the calculated schema kind. +func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { + if versioned, ok := into.(*runtime.VersionedObjects); ok { + into = versioned.Last() + obj, actual, err := s.Decode(originalData, gvk, into) + if err != nil { + return nil, actual, err + } + versioned.Objects = []runtime.Object{obj} + return versioned, actual, nil + } + + data := originalData + if s.yaml { + altered, err := yaml.YAMLToJSON(data) + if err != nil { + return nil, nil, err + } + data = altered + } + + actual, err := s.meta.Interpret(data) + if err != nil { + return nil, nil, err + } + + if gvk != nil { + // apply kind and version defaulting from provided default + if len(actual.Kind) == 0 { + actual.Kind = gvk.Kind + } + if len(actual.Version) == 0 && len(actual.Group) == 0 { + actual.Group = gvk.Group + actual.Version = gvk.Version + } + if len(actual.Version) == 0 && actual.Group == gvk.Group { + actual.Version = gvk.Version + } + } + + if unk, ok := into.(*runtime.Unknown); ok && unk != nil { + unk.RawJSON = originalData + // TODO: set content type here + unk.GetObjectKind().SetGroupVersionKind(actual) + return unk, actual, nil + } + + if into != nil { + typed, _, err := s.typer.ObjectKind(into) + switch { + case runtime.IsNotRegisteredError(err): + if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(into); err != nil { + return nil, actual, err + } + return into, actual, nil + case err != nil: + return nil, actual, err + default: + if len(actual.Kind) == 0 { + actual.Kind = typed.Kind + } + if len(actual.Version) == 0 && len(actual.Group) == 0 { + actual.Group = typed.Group + actual.Version = typed.Version + } + if len(actual.Version) == 0 && actual.Group == typed.Group { + actual.Version = typed.Version + } + } + } + + if len(actual.Kind) == 0 { + return nil, actual, runtime.NewMissingKindErr(string(originalData)) + } + if len(actual.Version) == 0 { + return nil, actual, runtime.NewMissingVersionErr(string(originalData)) + } + + // use the target if necessary + obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into) + if err != nil { + return nil, actual, err + } + + if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(obj); err != nil { + return nil, actual, err + } + return obj, actual, nil +} + +// EncodeToStream serializes the provided object to the given writer. Overrides is ignored. +func (s *Serializer) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unversioned.GroupVersion) error { + if s.yaml { + json, err := json.Marshal(obj) + if err != nil { + return err + } + data, err := yaml.JSONToYAML(json) + if err != nil { + return err + } + _, err = w.Write(data) + return err + } + + if s.pretty { + data, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return err + } + _, err = w.Write(data) + return err + } + encoder := json.NewEncoder(w) + return encoder.Encode(obj) +} + +// RecognizesData implements the RecognizingDecoder interface. +func (s *Serializer) RecognizesData(peek io.Reader) (bool, error) { + _, ok := utilyaml.GuessJSONStream(peek, 2048) + if s.yaml { + return !ok, nil + } + return ok, nil +} diff --git a/pkg/runtime/serializer/json/json_test.go b/pkg/runtime/serializer/json/json_test.go new file mode 100644 index 00000000000..f9a744f8718 --- /dev/null +++ b/pkg/runtime/serializer/json/json_test.go @@ -0,0 +1,269 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package json_test + +import ( + "fmt" + "reflect" + "strings" + "testing" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/conversion" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" + "k8s.io/kubernetes/pkg/util" +) + +type testDecodable struct { + Other string + Value int `json:"value"` + gvk *unversioned.GroupVersionKind +} + +func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d } +func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk } +func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk } + +func TestDecode(t *testing.T) { + testCases := []struct { + creater runtime.ObjectCreater + typer runtime.Typer + yaml bool + pretty bool + + data []byte + defaultGVK *unversioned.GroupVersionKind + into runtime.Object + + errFn func(error) bool + expectedObject runtime.Object + expectedGVK *unversioned.GroupVersionKind + }{ + { + data: []byte("{}"), + + expectedGVK: &unversioned.GroupVersionKind{}, + errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") }, + }, + { + data: []byte("{}"), + defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + creater: &mockCreater{err: fmt.Errorf("fake error")}, + + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + errFn: func(err error) bool { return err.Error() == "fake error" }, + }, + { + data: []byte("{}"), + defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + creater: &mockCreater{err: fmt.Errorf("fake error")}, + + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + errFn: func(err error) bool { return err.Error() == "fake error" }, + }, + { + data: []byte("{}"), + defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + creater: &mockCreater{obj: &testDecodable{}}, + expectedObject: &testDecodable{ + gvk: nil, // json serializer does NOT set GVK + }, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + }, + + // version without group is not defaulted + { + data: []byte(`{"apiVersion":"blah"}`), + defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + creater: &mockCreater{obj: &testDecodable{}}, + expectedObject: &testDecodable{ + gvk: nil, // json serializer does NOT set GVK + }, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"}, + }, + // group without version is defaulted + { + data: []byte(`{"apiVersion":"other/"}`), + defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + creater: &mockCreater{obj: &testDecodable{}}, + expectedObject: &testDecodable{ + gvk: nil, // json serializer does NOT set GVK + }, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + }, + + // accept runtime.Unknown as into and bypass creator + { + data: []byte(`{}`), + into: &runtime.Unknown{}, + + expectedGVK: &unversioned.GroupVersionKind{}, + expectedObject: &runtime.Unknown{ + RawJSON: []byte(`{}`), + }, + }, + { + data: []byte(`{"test":"object"}`), + into: &runtime.Unknown{}, + + expectedGVK: &unversioned.GroupVersionKind{}, + expectedObject: &runtime.Unknown{ + RawJSON: []byte(`{"test":"object"}`), + }, + }, + { + data: []byte(`{"test":"object"}`), + into: &runtime.Unknown{}, + defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + expectedObject: &runtime.Unknown{ + TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"}, + RawJSON: []byte(`{"test":"object"}`), + }, + }, + + // unregistered objects can be decoded into directly + { + data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`), + into: &testDecodable{}, + typer: &mockTyper{err: conversion.NewNotRegisteredErr(unversioned.GroupVersionKind{}, nil)}, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + expectedObject: &testDecodable{ + Other: "test", + Value: 1, + }, + }, + // registered types get defaulted by the into object kind + { + data: []byte(`{"value":1,"Other":"test"}`), + into: &testDecodable{}, + typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + expectedObject: &testDecodable{ + Other: "test", + Value: 1, + }, + }, + // registered types get defaulted by the into object kind even without version, but return an error + { + data: []byte(`{"value":1,"Other":"test"}`), + into: &testDecodable{}, + typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}}, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}, + errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") }, + expectedObject: &testDecodable{ + Other: "test", + Value: 1, + }, + }, + + // runtime.VersionedObjects are decoded + { + data: []byte(`{"value":1,"Other":"test"}`), + into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, + creater: &mockCreater{obj: &testDecodable{}}, + typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, + defaultGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + expectedObject: &runtime.VersionedObjects{ + Objects: []runtime.Object{ + &testDecodable{ + Other: "test", + Value: 1, + }, + }, + }, + }, + // runtime.VersionedObjects with an object are decoded into + { + data: []byte(`{"Other":"test"}`), + into: &runtime.VersionedObjects{Objects: []runtime.Object{&testDecodable{Value: 2}}}, + typer: &mockTyper{gvk: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}}, + expectedGVK: &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}, + expectedObject: &runtime.VersionedObjects{ + Objects: []runtime.Object{ + &testDecodable{ + Other: "test", + Value: 2, + }, + }, + }, + }, + } + + for i, test := range testCases { + var s runtime.Serializer + if test.yaml { + s = json.NewYAMLSerializer(json.DefaultMetaFactory, test.creater, test.typer) + } else { + s = json.NewSerializer(json.DefaultMetaFactory, test.creater, test.typer, test.pretty) + } + obj, gvk, err := s.Decode([]byte(test.data), test.defaultGVK, test.into) + + if !reflect.DeepEqual(test.expectedGVK, gvk) { + t.Errorf("%d: unexpected GVK: %v", i, gvk) + } + + switch { + case err == nil && test.errFn != nil: + t.Errorf("%d: failed: %v", i, err) + continue + case err != nil && test.errFn == nil: + t.Errorf("%d: failed: %v", i, err) + continue + case err != nil: + if !test.errFn(err) { + t.Errorf("%d: failed: %v", i, err) + } + if obj != nil { + t.Errorf("%d: should have returned nil object", i) + } + continue + } + + if test.into != nil && test.into != obj { + t.Errorf("%d: expected into to be returned: %v", i, obj) + continue + } + + if !reflect.DeepEqual(test.expectedObject, obj) { + t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.expectedObject, obj)) + } + } +} + +type mockCreater struct { + apiVersion string + kind string + err error + obj runtime.Object +} + +func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, error) { + c.apiVersion, c.kind = kind.GroupVersion().String(), kind.Kind + return c.obj, c.err +} + +type mockTyper struct { + gvk *unversioned.GroupVersionKind + err error +} + +func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) { + return t.gvk, false, t.err +} diff --git a/pkg/runtime/serializer/json/meta.go b/pkg/runtime/serializer/json/meta.go new file mode 100644 index 00000000000..91df105ed6c --- /dev/null +++ b/pkg/runtime/serializer/json/meta.go @@ -0,0 +1,61 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package json + +import ( + "encoding/json" + "fmt" + + "k8s.io/kubernetes/pkg/api/unversioned" +) + +// MetaFactory is used to store and retrieve the version and kind +// information for JSON objects in a serializer. +type MetaFactory interface { + // Interpret should return the version and kind of the wire-format of + // the object. + Interpret(data []byte) (*unversioned.GroupVersionKind, error) +} + +// DefaultMetaFactory is a default factory for versioning objects in JSON. The object +// in memory and in the default JSON serialization will use the "kind" and "apiVersion" +// fields. +var DefaultMetaFactory = SimpleMetaFactory{} + +// SimpleMetaFactory provides default methods for retrieving the type and version of objects +// that are identified with an "apiVersion" and "kind" fields in their JSON +// serialization. It may be parameterized with the names of the fields in memory, or an +// optional list of base structs to search for those fields in memory. +type SimpleMetaFactory struct { +} + +// Interpret will return the APIVersion and Kind of the JSON wire-format +// encoding of an object, or an error. +func (SimpleMetaFactory) Interpret(data []byte) (*unversioned.GroupVersionKind, error) { + findKind := struct { + APIVersion string `json:"apiVersion,omitempty"` + Kind string `json:"kind,omitempty"` + }{} + if err := json.Unmarshal(data, &findKind); err != nil { + return nil, fmt.Errorf("couldn't get version/kind; json parse error: %v", err) + } + gv, err := unversioned.ParseGroupVersion(findKind.APIVersion) + if err != nil { + return nil, err + } + return &unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: findKind.Kind}, nil +} diff --git a/pkg/runtime/serializer/json/meta_test.go b/pkg/runtime/serializer/json/meta_test.go new file mode 100644 index 00000000000..4b6351286f7 --- /dev/null +++ b/pkg/runtime/serializer/json/meta_test.go @@ -0,0 +1,45 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package json + +import "testing" + +func TestSimpleMetaFactoryInterpret(t *testing.T) { + factory := SimpleMetaFactory{} + gvk, err := factory.Interpret([]byte(`{"apiVersion":"1","kind":"object"}`)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if gvk.Version != "1" || gvk.Kind != "object" { + t.Errorf("unexpected interpret: %#v", gvk) + } + + // no kind or version + gvk, err = factory.Interpret([]byte(`{}`)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if gvk.Version != "" || gvk.Kind != "" { + t.Errorf("unexpected interpret: %#v", gvk) + } + + // unparsable + gvk, err = factory.Interpret([]byte(`{`)) + if err == nil { + t.Errorf("unexpected non-error") + } +} diff --git a/pkg/runtime/serializer/recognizer/recognizer.go b/pkg/runtime/serializer/recognizer/recognizer.go new file mode 100644 index 00000000000..14a2cb3e841 --- /dev/null +++ b/pkg/runtime/serializer/recognizer/recognizer.go @@ -0,0 +1,79 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package recognizer + +import ( + "bytes" + "fmt" + "io" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" +) + +type RecognizingDecoder interface { + runtime.Decoder + RecognizesData(peek io.Reader) (bool, error) +} + +func NewDecoder(decoders ...runtime.Decoder) runtime.Decoder { + recognizing, blind := []RecognizingDecoder{}, []runtime.Decoder{} + for _, d := range decoders { + if r, ok := d.(RecognizingDecoder); ok { + recognizing = append(recognizing, r) + } else { + blind = append(blind, d) + } + } + return &decoder{ + recognizing: recognizing, + blind: blind, + } +} + +type decoder struct { + recognizing []RecognizingDecoder + blind []runtime.Decoder +} + +func (d *decoder) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { + var lastErr error + for _, r := range d.recognizing { + buf := bytes.NewBuffer(data) + ok, err := r.RecognizesData(buf) + if err != nil { + lastErr = err + continue + } + if !ok { + continue + } + return r.Decode(data, gvk, into) + } + for _, d := range d.blind { + out, actual, err := d.Decode(data, gvk, into) + if err != nil { + lastErr = err + continue + } + return out, actual, nil + } + if lastErr == nil { + lastErr = fmt.Errorf("no serialization format matched the provided data") + } + return nil, nil, lastErr +} diff --git a/pkg/runtime/serializer/recognizer/recognizer_test.go b/pkg/runtime/serializer/recognizer/recognizer_test.go new file mode 100644 index 00000000000..c83b87aa4dd --- /dev/null +++ b/pkg/runtime/serializer/recognizer/recognizer_test.go @@ -0,0 +1,57 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package recognizer + +import ( + "testing" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" +) + +type A struct{} + +func (A) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind } + +func TestRecognizer(t *testing.T) { + s := runtime.NewScheme() + s.AddKnownTypes(unversioned.GroupVersion{Version: "v1"}, &A{}) + d := NewDecoder( + json.NewSerializer(json.DefaultMetaFactory, s, runtime.ObjectTyperToTyper(s), false), + json.NewYAMLSerializer(json.DefaultMetaFactory, s, runtime.ObjectTyperToTyper(s)), + ) + out, _, err := d.Decode([]byte(` +kind: A +apiVersion: v1 +`), nil, nil) + if err != nil { + t.Fatal(err) + } + t.Logf("%#v", out) + + out, _, err = d.Decode([]byte(` +{ + "kind":"A", + "apiVersion":"v1" +} +`), nil, nil) + if err != nil { + t.Fatal(err) + } + t.Logf("%#v", out) +} diff --git a/pkg/runtime/serializer/versioning/versioning.go b/pkg/runtime/serializer/versioning/versioning.go new file mode 100644 index 00000000000..1b501369c13 --- /dev/null +++ b/pkg/runtime/serializer/versioning/versioning.go @@ -0,0 +1,243 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package versioning + +import ( + "fmt" + "io" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" +) + +// NewCodecForScheme is a convenience method for callers that are using a scheme. +func NewCodecForScheme( + // TODO: I should be a scheme interface? + scheme *runtime.Scheme, + serializer runtime.Serializer, + encodeVersion []unversioned.GroupVersion, + decodeVersion []unversioned.GroupVersion, +) runtime.Codec { + return NewCodec(serializer, scheme, scheme, scheme, runtime.ObjectTyperToTyper(scheme), encodeVersion, decodeVersion) +} + +// NewCodec takes objects in their internal versions and converts them to external versions before +// serializing them. It assumes the serializer provided to it only deals with external versions. +// This class is also a serializer, but is generally used with a specific version. +func NewCodec( + serializer runtime.Serializer, + convertor runtime.ObjectConvertor, + creater runtime.ObjectCreater, + copier runtime.ObjectCopier, + typer runtime.Typer, + encodeVersion []unversioned.GroupVersion, + decodeVersion []unversioned.GroupVersion, +) runtime.Codec { + internal := &codec{ + serializer: serializer, + convertor: convertor, + creater: creater, + copier: copier, + typer: typer, + } + if encodeVersion != nil { + internal.encodeVersion = make(map[string]unversioned.GroupVersion) + for _, v := range encodeVersion { + internal.encodeVersion[v.Group] = v + } + } + if decodeVersion != nil { + internal.decodeVersion = make(map[string]unversioned.GroupVersion) + for _, v := range decodeVersion { + internal.decodeVersion[v.Group] = v + } + } + + return internal +} + +type codec struct { + serializer runtime.Serializer + convertor runtime.ObjectConvertor + creater runtime.ObjectCreater + copier runtime.ObjectCopier + typer runtime.Typer + + encodeVersion map[string]unversioned.GroupVersion + decodeVersion map[string]unversioned.GroupVersion +} + +// Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is +// successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an +// into that matches the serialized version. +func (c *codec) Decode(data []byte, defaultGVK *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { + versioned, isVersioned := into.(*runtime.VersionedObjects) + if isVersioned { + into = versioned.Last() + } + + obj, gvk, err := c.serializer.Decode(data, defaultGVK, into) + if err != nil { + return nil, gvk, err + } + + // if we specify a target, use generic conversion. + if into != nil { + if into == obj { + if isVersioned { + return versioned, gvk, nil + } + return into, gvk, nil + } + if err := c.convertor.Convert(obj, into); err != nil { + return nil, gvk, err + } + if isVersioned { + versioned.Objects = []runtime.Object{obj, into} + return versioned, gvk, nil + } + return into, gvk, nil + } + + // invoke a version conversion + group := gvk.Group + if defaultGVK != nil { + group = defaultGVK.Group + } + var targetGV unversioned.GroupVersion + if c.decodeVersion == nil { + // convert to internal by default + targetGV.Group = group + targetGV.Version = runtime.APIVersionInternal + } else { + gv, ok := c.decodeVersion[group] + if !ok { + // unknown objects are left in their original version + if isVersioned { + versioned.Objects = []runtime.Object{obj} + return versioned, gvk, nil + } + return obj, gvk, nil + } + targetGV = gv + } + + if gvk.GroupVersion() == targetGV { + if isVersioned { + versioned.Objects = []runtime.Object{obj} + return versioned, gvk, nil + } + return obj, gvk, nil + } + + if isVersioned { + // create a copy, because ConvertToVersion does not guarantee non-mutation of objects + copied, err := c.copier.Copy(obj) + if err != nil { + copied = obj + } + versioned.Objects = []runtime.Object{copied} + } + + // Convert if needed. + out, err := c.convertor.ConvertToVersion(obj, targetGV.String()) + if err != nil { + return nil, gvk, err + } + if isVersioned { + versioned.Objects = append(versioned.Objects, out) + return versioned, gvk, nil + } + return out, gvk, nil +} + +// EncodeToStream ensures the provided object is output in the right scheme. If overrides are specified, when +// encoding the object the first override that matches the object's group is used. Other overrides are ignored. +func (c *codec) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unversioned.GroupVersion) error { + if _, ok := obj.(*runtime.Unknown); ok { + return c.serializer.EncodeToStream(obj, w, overrides...) + } + gvk, isUnversioned, err := c.typer.ObjectKind(obj) + if err != nil { + return err + } + + if (c.encodeVersion == nil && len(overrides) == 0) || isUnversioned { + old := obj.GetObjectKind().GroupVersionKind() + obj.GetObjectKind().SetGroupVersionKind(gvk) + defer obj.GetObjectKind().SetGroupVersionKind(old) + return c.serializer.EncodeToStream(obj, w, overrides...) + } + + targetGV, ok := c.encodeVersion[gvk.Group] + // use override if provided + for i, override := range overrides { + if override.Group == gvk.Group { + ok = true + targetGV = override + // swap the position of the override + overrides[0], overrides[i] = targetGV, overrides[0] + break + } + } + + // attempt a conversion to the sole encode version + if !ok && len(c.encodeVersion) == 1 { + ok = true + for _, v := range c.encodeVersion { + targetGV = v + } + // ensure the target override is first + overrides = promoteOrPrependGroupVersion(targetGV, overrides) + } + + // if no fallback is available, error + if !ok { + return fmt.Errorf("the codec does not recognize group %q for kind %q and cannot encode it", gvk.Group, gvk.Kind) + } + + // Perform a conversion if necessary + if gvk.GroupVersion() != targetGV { + out, err := c.convertor.ConvertToVersion(obj, targetGV.String()) + if err != nil { + if ok { + return err + } + } else { + obj = out + } + } else { + old := obj.GetObjectKind().GroupVersionKind() + defer obj.GetObjectKind().SetGroupVersionKind(old) + obj.GetObjectKind().SetGroupVersionKind(&unversioned.GroupVersionKind{Group: targetGV.Group, Version: targetGV.Version, Kind: gvk.Kind}) + } + + return c.serializer.EncodeToStream(obj, w, overrides...) +} + +// promoteOrPrependGroupVersion finds the group version in the provided group versions that has the same group as target. +// If the group is found the returned array will have that group version in the first position - if the group is not found +// the returned array will have target in the first position. +func promoteOrPrependGroupVersion(target unversioned.GroupVersion, gvs []unversioned.GroupVersion) []unversioned.GroupVersion { + for i, gv := range gvs { + if gv.Group == target.Group { + gvs[0], gvs[i] = gvs[i], gvs[0] + return gvs + } + } + return append([]unversioned.GroupVersion{target}, gvs...) +} diff --git a/pkg/runtime/serializer/versioning/versioning_test.go b/pkg/runtime/serializer/versioning/versioning_test.go new file mode 100644 index 00000000000..3d7eba27e3c --- /dev/null +++ b/pkg/runtime/serializer/versioning/versioning_test.go @@ -0,0 +1,300 @@ +/* +Copyright 2015 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package versioning + +import ( + "fmt" + "io" + "reflect" + "testing" + + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util" +) + +type testDecodable struct { + Other string + Value int `json:"value"` + gvk *unversioned.GroupVersionKind +} + +func (d *testDecodable) GetObjectKind() unversioned.ObjectKind { return d } +func (d *testDecodable) SetGroupVersionKind(gvk *unversioned.GroupVersionKind) { d.gvk = gvk } +func (d *testDecodable) GroupVersionKind() *unversioned.GroupVersionKind { return d.gvk } + +func TestDecode(t *testing.T) { + gvk1 := &unversioned.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"} + decodable1 := &testDecodable{} + decodable2 := &testDecodable{} + decodable3 := &testDecodable{} + versionedDecodable1 := &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}} + + testCases := []struct { + serializer runtime.Serializer + convertor runtime.ObjectConvertor + creater runtime.ObjectCreater + copier runtime.ObjectCopier + typer runtime.Typer + yaml bool + pretty bool + + encodes, decodes []unversioned.GroupVersion + + defaultGVK *unversioned.GroupVersionKind + into runtime.Object + + errFn func(error) bool + expectedObject runtime.Object + sameObject runtime.Object + expectedGVK *unversioned.GroupVersionKind + }{ + { + serializer: &mockSerializer{actual: gvk1}, + convertor: &checkConvertor{groupVersion: "other/__internal"}, + expectedGVK: gvk1, + }, + { + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"}, + expectedGVK: gvk1, + sameObject: decodable2, + }, + // defaultGVK.Group is allowed to force a conversion to the destination group + { + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + defaultGVK: &unversioned.GroupVersionKind{Group: "force"}, + convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "force/__internal"}, + expectedGVK: gvk1, + sameObject: decodable2, + }, + // uses direct conversion for into when objects differ + { + into: decodable3, + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + convertor: &checkConvertor{in: decodable1, obj: decodable3, directConvert: true}, + expectedGVK: gvk1, + sameObject: decodable3, + }, + { + into: versionedDecodable1, + serializer: &mockSerializer{actual: gvk1, obj: decodable3}, + convertor: &checkConvertor{in: decodable3, obj: decodable1, directConvert: true}, + expectedGVK: gvk1, + sameObject: versionedDecodable1, + }, + // returns directly when serializer returns into + { + into: decodable3, + serializer: &mockSerializer{actual: gvk1, obj: decodable3}, + expectedGVK: gvk1, + sameObject: decodable3, + }, + // returns directly when serializer returns into + { + into: versionedDecodable1, + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + expectedGVK: gvk1, + sameObject: versionedDecodable1, + }, + + // runtime.VersionedObjects are decoded + { + into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, + + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + copier: &checkCopy{in: decodable1, obj: decodable1}, + convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"}, + expectedGVK: gvk1, + expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}}, + }, + { + into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, + + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + copier: &checkCopy{in: decodable1, obj: nil, err: fmt.Errorf("error on copy")}, + convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: "other/__internal"}, + expectedGVK: gvk1, + expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}}, + }, + + // decode into the same version as the serialized object + { + decodes: []unversioned.GroupVersion{gvk1.GroupVersion()}, + + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + expectedGVK: gvk1, + expectedObject: decodable1, + }, + { + into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, + decodes: []unversioned.GroupVersion{gvk1.GroupVersion()}, + + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + expectedGVK: gvk1, + expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}}, + }, + + // codec with non matching version skips conversion altogether + { + decodes: []unversioned.GroupVersion{{Group: "something", Version: "else"}}, + + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + expectedGVK: gvk1, + expectedObject: decodable1, + }, + { + into: &runtime.VersionedObjects{Objects: []runtime.Object{}}, + decodes: []unversioned.GroupVersion{{Group: "something", Version: "else"}}, + + serializer: &mockSerializer{actual: gvk1, obj: decodable1}, + expectedGVK: gvk1, + expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}}, + }, + } + + for i, test := range testCases { + t.Logf("%d", i) + s := NewCodec(test.serializer, test.convertor, test.creater, test.copier, test.typer, test.encodes, test.decodes) + obj, gvk, err := s.Decode([]byte(`{}`), test.defaultGVK, test.into) + + if !reflect.DeepEqual(test.expectedGVK, gvk) { + t.Errorf("%d: unexpected GVK: %v", i, gvk) + } + + switch { + case err == nil && test.errFn != nil: + t.Errorf("%d: failed: %v", i, err) + continue + case err != nil && test.errFn == nil: + t.Errorf("%d: failed: %v", i, err) + continue + case err != nil: + if !test.errFn(err) { + t.Errorf("%d: failed: %v", i, err) + } + if obj != nil { + t.Errorf("%d: should have returned nil object", i) + } + continue + } + + if test.into != nil && test.into != obj { + t.Errorf("%d: expected into to be returned: %v", i, obj) + continue + } + + switch { + case test.expectedObject != nil: + if !reflect.DeepEqual(test.expectedObject, obj) { + t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.expectedObject, obj)) + } + case test.sameObject != nil: + if test.sameObject != obj { + t.Errorf("%d: unexpected object:\n%s", i, util.ObjectGoPrintSideBySide(test.sameObject, obj)) + } + case obj != nil: + t.Errorf("%d: unexpected object: %#v", i, obj) + } + } +} + +type checkCopy struct { + in, obj runtime.Object + err error +} + +func (c *checkCopy) Copy(obj runtime.Object) (runtime.Object, error) { + if c.in != nil && c.in != obj { + return nil, fmt.Errorf("unexpected input to copy: %#v", obj) + } + return c.obj, c.err +} + +type checkConvertor struct { + err error + in, obj runtime.Object + groupVersion string + directConvert bool +} + +func (c *checkConvertor) Convert(in, out interface{}) error { + if !c.directConvert { + return fmt.Errorf("unexpected call to Convert") + } + if c.in != nil && c.in != in { + return fmt.Errorf("unexpected in: %s", in) + } + if c.obj != nil && c.obj != out { + return fmt.Errorf("unexpected out: %s", out) + } + return c.err +} +func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion string) (out runtime.Object, err error) { + if c.directConvert { + return nil, fmt.Errorf("unexpected call to ConvertToVersion") + } + if c.in != nil && c.in != in { + return nil, fmt.Errorf("unexpected in: %s", in) + } + if c.groupVersion != outVersion { + return nil, fmt.Errorf("unexpected outversion: %s", outVersion) + } + return c.obj, c.err +} +func (c *checkConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) { + return "", "", fmt.Errorf("unexpected call to ConvertFieldLabel") +} + +type mockSerializer struct { + err error + obj runtime.Object + versions []unversioned.GroupVersion + + defaults, actual *unversioned.GroupVersionKind + into runtime.Object +} + +func (s *mockSerializer) Decode(data []byte, defaults *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { + s.defaults = defaults + s.into = into + return s.obj, s.actual, s.err +} + +func (s *mockSerializer) EncodeToStream(obj runtime.Object, w io.Writer, versions ...unversioned.GroupVersion) error { + s.obj = obj + s.versions = versions + return s.err +} + +type mockCreater struct { + err error + obj runtime.Object +} + +func (c *mockCreater) New(kind unversioned.GroupVersionKind) (runtime.Object, error) { + return c.obj, c.err +} + +type mockTyper struct { + gvk *unversioned.GroupVersionKind + err error +} + +func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) { + return t.gvk, false, t.err +} diff --git a/pkg/runtime/serializer/yaml/yaml.go b/pkg/runtime/serializer/yaml/yaml.go new file mode 100644 index 00000000000..637c777be92 --- /dev/null +++ b/pkg/runtime/serializer/yaml/yaml.go @@ -0,0 +1,46 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package yaml + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/yaml" +) + +// yamlSerializer converts YAML passed to the Decoder methods to JSON. +type yamlSerializer struct { + // the nested serializer + runtime.Serializer +} + +// yamlSerializer implements Serializer +var _ runtime.Serializer = yamlSerializer{} + +// NewDecodingSerializer adds YAML decoding support to a serializer that supports JSON. +func NewDecodingSerializer(jsonSerializer runtime.Serializer) runtime.Serializer { + return &yamlSerializer{jsonSerializer} +} + +func (c yamlSerializer) Decode(data []byte, gvk *unversioned.GroupVersionKind, into runtime.Object) (runtime.Object, *unversioned.GroupVersionKind, error) { + out, err := yaml.ToJSON(data) + if err != nil { + return nil, nil, err + } + data = out + return c.Serializer.Decode(data, gvk, into) +} diff --git a/pkg/runtime/types.go b/pkg/runtime/types.go index a09af5e14f8..3b8cede446a 100644 --- a/pkg/runtime/types.go +++ b/pkg/runtime/types.go @@ -36,39 +36,18 @@ type TypeMeta struct { Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` } -// PluginBase is like TypeMeta, but it's intended for plugin objects that won't ever be encoded -// except while embedded in other objects. -type PluginBase struct { - Kind string `json:"kind,omitempty"` -} - -// EmbeddedObject has appropriate encoder and decoder functions, such that on the wire, it's -// stored as a []byte, but in memory, the contained object is accessible as an Object -// via the Get() function. Only valid API objects may be stored via EmbeddedObject. -// The purpose of this is to allow an API object of type known only at runtime to be -// embedded within other API objects. -// -// Note that object assumes that you've registered all of your api types with the api package. -// -// EmbeddedObject and RawExtension can be used together to allow for API object extensions: -// see the comment for RawExtension. -type EmbeddedObject struct { - Object -} - -// RawExtension is used with EmbeddedObject to do a two-phase encoding of extension objects. +// RawExtension is used to hold extensions in external versions. // // To use this, make a field which has RawExtension as its type in your external, versioned -// struct, and EmbeddedObject in your internal struct. You also need to register your +// struct, and Object in your internal struct. You also need to register your // various plugin types. // // // Internal package: // type MyAPIObject struct { // runtime.TypeMeta `json:",inline"` -// MyPlugin runtime.EmbeddedObject `json:"myPlugin"` +// MyPlugin runtime.Object `json:"myPlugin"` // } // type PluginA struct { -// runtime.PluginBase `json:",inline"` // AOption string `json:"aOption"` // } // @@ -78,7 +57,6 @@ type EmbeddedObject struct { // MyPlugin runtime.RawExtension `json:"myPlugin"` // } // type PluginA struct { -// runtime.PluginBase `json:",inline"` // AOption string `json:"aOption"` // } // @@ -97,12 +75,16 @@ type EmbeddedObject struct { // The next step is to copy (using pkg/conversion) into the internal struct. The runtime // package's DefaultScheme has conversion functions installed which will unpack the // JSON stored in RawExtension, turning it into the correct object type, and storing it -// in the EmbeddedObject. (TODO: In the case where the object is of an unknown type, a +// in the Object. (TODO: In the case where the object is of an unknown type, a // runtime.Unknown object will be created and stored.) // // +protobuf=true type RawExtension struct { + // RawJSON is the underlying serialization of this object. RawJSON []byte + // Object can hold a representation of this extension - useful for working with versioned + // structs. + Object Object `json:"-"` } // Unknown allows api objects with unknown types to be passed-through. This can be used @@ -131,3 +113,13 @@ type Unstructured struct { // children. Object map[string]interface{} } + +// VersionedObjects is used by Decoders to give callers a way to access all versions +// of an object during the decoding process. +type VersionedObjects struct { + // Objects is the set of objects retrieved during decoding, in order of conversion. + // The 0 index is the object as serialized on the wire. If conversion has occured, + // other objects may be present. The right most object is the same as would be returned + // by a normal Decode call. + Objects []Object +} diff --git a/pkg/runtime/unstructured.go b/pkg/runtime/unstructured.go index 158961fd8ef..a236e9adb6b 100644 --- a/pkg/runtime/unstructured.go +++ b/pkg/runtime/unstructured.go @@ -18,9 +18,7 @@ package runtime import ( "encoding/json" - "fmt" - "net/url" - "reflect" + "io" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/conversion" @@ -28,36 +26,19 @@ import ( // UnstructuredJSONScheme is capable of converting JSON data into the Unstructured // type, which can be used for generic access to objects without a predefined scheme. -var UnstructuredJSONScheme ObjectDecoder = unstructuredJSONScheme{} +// TODO: move into serializer/json. +var UnstructuredJSONScheme Decoder = unstructuredJSONScheme{} type unstructuredJSONScheme struct{} -var _ Decoder = unstructuredJSONScheme{} -var _ ObjectDecoder = unstructuredJSONScheme{} +var _ Codec = unstructuredJSONScheme{} -// Recognizes returns true for any version or kind that is specified (internal -// versions are specifically excluded). -func (unstructuredJSONScheme) Recognizes(gvk unversioned.GroupVersionKind) bool { - return !gvk.GroupVersion().IsEmpty() && len(gvk.Kind) > 0 -} - -func (s unstructuredJSONScheme) Decode(data []byte) (Object, error) { +func (s unstructuredJSONScheme) Decode(data []byte, _ *unversioned.GroupVersionKind, _ Object) (Object, *unversioned.GroupVersionKind, error) { unstruct := &Unstructured{} - if err := DecodeInto(s, data, unstruct); err != nil { - return nil, err - } - return unstruct, nil -} - -func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error { - unstruct, ok := obj.(*Unstructured) - if !ok { - return fmt.Errorf("the unstructured JSON scheme does not recognize %v", reflect.TypeOf(obj)) - } m := make(map[string]interface{}) if err := json.Unmarshal(data, &m); err != nil { - return err + return nil, nil, err } if v, ok := m["kind"]; ok { if s, ok := v.(string); ok { @@ -69,44 +50,30 @@ func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error { unstruct.APIVersion = s } } + if len(unstruct.APIVersion) == 0 { - return conversion.NewMissingVersionErr(string(data)) + return nil, nil, conversion.NewMissingVersionErr(string(data)) } + gv, err := unversioned.ParseGroupVersion(unstruct.APIVersion) + if err != nil { + return nil, nil, err + } + gvk := gv.WithKind(unstruct.Kind) if len(unstruct.Kind) == 0 { - return conversion.NewMissingKindErr(string(data)) + return nil, &gvk, conversion.NewMissingKindErr(string(data)) } unstruct.Object = m - return nil + return unstruct, &gvk, nil } -func (unstructuredJSONScheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, gvk unversioned.GroupVersionKind) error { - return nil -} - -func (unstructuredJSONScheme) DecodeToVersion(data []byte, gv unversioned.GroupVersion) (Object, error) { - return nil, nil -} - -func (unstructuredJSONScheme) DecodeParametersInto(paramaters url.Values, obj Object) error { - return nil -} - -func (unstructuredJSONScheme) DataKind(data []byte) (unversioned.GroupVersionKind, error) { - obj := TypeMeta{} - if err := json.Unmarshal(data, &obj); err != nil { - return unversioned.GroupVersionKind{}, err +func (s unstructuredJSONScheme) EncodeToStream(obj Object, w io.Writer, overrides ...unversioned.GroupVersion) error { + switch t := obj.(type) { + case *Unstructured: + return json.NewEncoder(w).Encode(t.Object) + case *Unknown: + _, err := w.Write(t.RawJSON) + return err + default: + return json.NewEncoder(w).Encode(t) } - if len(obj.APIVersion) == 0 { - return unversioned.GroupVersionKind{}, conversion.NewMissingVersionErr(string(data)) - } - if len(obj.Kind) == 0 { - return unversioned.GroupVersionKind{}, conversion.NewMissingKindErr(string(data)) - } - - gv, err := unversioned.ParseGroupVersion(obj.APIVersion) - if err != nil { - return unversioned.GroupVersionKind{}, err - } - - return gv.WithKind(obj.Kind), nil } diff --git a/pkg/runtime/unstructured_test.go b/pkg/runtime/unstructured_test.go index 120e3ff80a5..cca0fe25119 100644 --- a/pkg/runtime/unstructured_test.go +++ b/pkg/runtime/unstructured_test.go @@ -42,7 +42,7 @@ func TestDecodeUnstructured(t *testing.T) { if pod, ok := pl.Items[1].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" { t.Errorf("object not converted: %#v", pl.Items[1]) } - if _, ok := pl.Items[2].(*runtime.Unknown); !ok { - t.Errorf("object should not have been converted: %#v", pl.Items[2]) + if pod, ok := pl.Items[2].(*runtime.Unstructured); !ok || pod.Object["kind"] != "Pod" || pod.Object["metadata"].(map[string]interface{})["name"] != "test" { + t.Errorf("object not converted: %#v", pl.Items[2]) } } diff --git a/pkg/storage/etcd/etcd_helper.go b/pkg/storage/etcd/etcd_helper.go index 7e4fcac21f0..960c1b71207 100644 --- a/pkg/storage/etcd/etcd_helper.go +++ b/pkg/storage/etcd/etcd_helper.go @@ -148,7 +148,7 @@ func (h *etcdHelper) Create(ctx context.Context, key string, obj, out runtime.Ob glog.Errorf("Context is nil") } key = h.prefixEtcdKey(key) - data, err := h.codec.Encode(obj) + data, err := runtime.Encode(h.codec, obj) if err != nil { return err } @@ -183,7 +183,7 @@ func (h *etcdHelper) Set(ctx context.Context, key string, obj, out runtime.Objec glog.Errorf("Context is nil") } var response *etcd.Response - data, err := h.codec.Encode(obj) + data, err := runtime.Encode(h.codec, obj) if err != nil { return err } @@ -333,7 +333,13 @@ func (h *etcdHelper) extractObj(response *etcd.Response, inErr error, objPtr run return "", nil, fmt.Errorf("unable to locate a value on the response: %#v", response) } body = node.Value - err = h.codec.DecodeInto([]byte(body), objPtr) + out, gvk, err := h.codec.Decode([]byte(body), nil, objPtr) + if err != nil { + return body, nil, err + } + if out != objPtr { + return body, nil, fmt.Errorf("unable to decode object %s into %v", gvk.String(), reflect.TypeOf(objPtr)) + } if h.versioner != nil { _ = h.versioner.UpdateObject(objPtr, node.Expiration, node.ModifiedIndex) // being unable to set the version does not prevent the object from being extracted @@ -403,19 +409,19 @@ func (h *etcdHelper) decodeNodeList(nodes []*etcd.Node, filter storage.FilterFun v.Set(reflect.Append(v, reflect.ValueOf(obj).Elem())) } } else { - obj := reflect.New(v.Type().Elem()) - if err := h.codec.DecodeInto([]byte(node.Value), obj.Interface().(runtime.Object)); err != nil { + obj, _, err := h.codec.Decode([]byte(node.Value), nil, reflect.New(v.Type().Elem()).Interface().(runtime.Object)) + if err != nil { return err } if h.versioner != nil { // being unable to set the version does not prevent the object from being extracted - _ = h.versioner.UpdateObject(obj.Interface().(runtime.Object), node.Expiration, node.ModifiedIndex) + _ = h.versioner.UpdateObject(obj, node.Expiration, node.ModifiedIndex) } - if filter(obj.Interface().(runtime.Object)) { - v.Set(reflect.Append(v, obj.Elem())) + if filter(obj) { + v.Set(reflect.Append(v, reflect.ValueOf(obj).Elem())) } if node.ModifiedIndex != 0 { - h.addToCache(node.ModifiedIndex, obj.Interface().(runtime.Object)) + h.addToCache(node.ModifiedIndex, obj) } } } @@ -532,7 +538,7 @@ func (h *etcdHelper) GuaranteedUpdate(ctx context.Context, key string, ptrToType ttl = *newTTL } - data, err := h.codec.Encode(ret) + data, err := runtime.Encode(h.codec, ret) if err != nil { return err } diff --git a/pkg/storage/etcd/etcd_helper_test.go b/pkg/storage/etcd/etcd_helper_test.go index 469e0769514..253f121d429 100644 --- a/pkg/storage/etcd/etcd_helper_test.go +++ b/pkg/storage/etcd/etcd_helper_test.go @@ -21,6 +21,7 @@ import ( "reflect" "sync" "testing" + "time" etcd "github.com/coreos/etcd/client" "github.com/stretchr/testify/assert" @@ -30,6 +31,7 @@ import ( apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" "k8s.io/kubernetes/pkg/storage" "k8s.io/kubernetes/pkg/storage/etcd/etcdtest" etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing" @@ -39,20 +41,25 @@ import ( const validEtcdVersion = "etcd 2.0.9" -var scheme *runtime.Scheme -var codec runtime.Codec - -func init() { - scheme = runtime.NewScheme() - scheme.AddKnownTypes(testapi.Default.InternalGroupVersion(), &storagetesting.TestResource{}) +func testScheme(t *testing.T) (*runtime.Scheme, runtime.Codec) { + scheme := runtime.NewScheme() + scheme.Log(t) scheme.AddKnownTypes(*testapi.Default.GroupVersion(), &storagetesting.TestResource{}) - codec = runtime.CodecFor(scheme, *testapi.Default.GroupVersion()) - scheme.AddConversionFuncs( + scheme.AddKnownTypes(testapi.Default.InternalGroupVersion(), &storagetesting.TestResource{}) + if err := scheme.AddConversionFuncs( func(in *storagetesting.TestResource, out *storagetesting.TestResource, s conversion.Scope) error { *out = *in return nil }, - ) + func(in, out *time.Time, s conversion.Scope) error { + *out = *in + return nil + }, + ); err != nil { + panic(err) + } + codec := serializer.NewCodecFactory(scheme).LegacyCodec(*testapi.Default.GroupVersion()) + return scheme, codec } func newEtcdHelper(client etcd.Client, codec runtime.Codec, prefix string) etcdHelper { @@ -61,7 +68,7 @@ func newEtcdHelper(client etcd.Client, codec runtime.Codec, prefix string) etcdH // Returns an encoded version of api.Pod with the given name. func getEncodedPod(name string) string { - pod, _ := testapi.Default.Codec().Encode(&api.Pod{ + pod, _ := runtime.Encode(testapi.Default.Codec(), &api.Pod{ ObjectMeta: api.ObjectMeta{Name: name}, }) return string(pod) @@ -258,7 +265,7 @@ func TestCreate(t *testing.T) { if err != nil { t.Errorf("Unexpected error %#v", err) } - _, err = testapi.Default.Codec().Encode(obj) + _, err = runtime.Encode(testapi.Default.Codec(), obj) if err != nil { t.Errorf("Unexpected error %#v", err) } @@ -266,7 +273,7 @@ func TestCreate(t *testing.T) { if err != nil { t.Errorf("Unexpected error %#v", err) } - _, err = testapi.Default.Codec().Encode(returnedObj) + _, err = runtime.Encode(testapi.Default.Codec(), returnedObj) if err != nil { t.Errorf("Unexpected error %#v", err) } @@ -374,6 +381,7 @@ func TestSetNilOutParam(t *testing.T) { } func TestGuaranteedUpdate(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") @@ -418,6 +426,7 @@ func TestGuaranteedUpdate(t *testing.T) { } func TestGuaranteedUpdateNoChange(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") @@ -447,6 +456,7 @@ func TestGuaranteedUpdateNoChange(t *testing.T) { } func TestGuaranteedUpdateKeyNotFound(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") @@ -473,6 +483,7 @@ func TestGuaranteedUpdateKeyNotFound(t *testing.T) { } func TestGuaranteedUpdate_CreateCollision(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := etcdtest.AddPrefix("/some/key") diff --git a/pkg/storage/etcd/etcd_watcher.go b/pkg/storage/etcd/etcd_watcher.go index 4c807dac356..0411afe8e33 100644 --- a/pkg/storage/etcd/etcd_watcher.go +++ b/pkg/storage/etcd/etcd_watcher.go @@ -17,6 +17,7 @@ limitations under the License. package etcd import ( + "fmt" "net/http" "sync" "sync/atomic" @@ -210,7 +211,7 @@ func etcdGetInitialWatchState(ctx context.Context, client etcd.KeysAPI, key stri resp, err := client.Get(ctx, key, &opts) if err != nil { if !etcdutil.IsEtcdNotFound(err) { - glog.Errorf("watch was unable to retrieve the current index for the provided key (%q): %v", key, err) + util.HandleError(fmt.Errorf("watch was unable to retrieve the current index for the provided key (%q): %v", key, err)) return resourceVersion, err } if etcdError, ok := err.(etcd.Error); ok { @@ -300,7 +301,7 @@ func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { return obj, nil } - obj, err := w.encoding.Decode([]byte(node.Value)) + obj, err := runtime.Decode(w.encoding, []byte(node.Value)) if err != nil { return nil, err } @@ -308,7 +309,7 @@ func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { // ensure resource version is set on the object we load from etcd if w.versioner != nil { if err := w.versioner.UpdateObject(obj, node.Expiration, node.ModifiedIndex); err != nil { - glog.Errorf("failure to version api object (%d) %#v: %v", node.ModifiedIndex, obj, err) + util.HandleError(fmt.Errorf("failure to version api object (%d) %#v: %v", node.ModifiedIndex, obj, err)) } } @@ -316,7 +317,7 @@ func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { if w.transform != nil { obj, err = w.transform(obj) if err != nil { - glog.Errorf("failure to transform api object %#v: %v", obj, err) + util.HandleError(fmt.Errorf("failure to transform api object %#v: %v", obj, err)) return nil, err } } @@ -329,7 +330,7 @@ func (w *etcdWatcher) decodeObject(node *etcd.Node) (runtime.Object, error) { func (w *etcdWatcher) sendAdd(res *etcd.Response) { if res.Node == nil { - glog.Errorf("unexpected nil node: %#v", res) + util.HandleError(fmt.Errorf("unexpected nil node: %#v", res)) return } if w.include != nil && !w.include(res.Node.Key) { @@ -337,7 +338,7 @@ func (w *etcdWatcher) sendAdd(res *etcd.Response) { } obj, err := w.decodeObject(res.Node) if err != nil { - glog.Errorf("failure to decode api object: '%v' from %#v %#v", string(res.Node.Value), res, res.Node) + util.HandleError(fmt.Errorf("failure to decode api object: %v\n'%v' from %#v %#v", err, string(res.Node.Value), res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. @@ -366,7 +367,7 @@ func (w *etcdWatcher) sendModify(res *etcd.Response) { } curObj, err := w.decodeObject(res.Node) if err != nil { - glog.Errorf("failure to decode api object: '%v' from %#v %#v", string(res.Node.Value), res, res.Node) + util.HandleError(fmt.Errorf("failure to decode api object: %v\n'%v' from %#v %#v", err, string(res.Node.Value), res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. @@ -406,7 +407,7 @@ func (w *etcdWatcher) sendModify(res *etcd.Response) { func (w *etcdWatcher) sendDelete(res *etcd.Response) { if res.PrevNode == nil { - glog.Errorf("unexpected nil prev node: %#v", res) + util.HandleError(fmt.Errorf("unexpected nil prev node: %#v", res)) return } if w.include != nil && !w.include(res.PrevNode.Key) { @@ -421,7 +422,7 @@ func (w *etcdWatcher) sendDelete(res *etcd.Response) { } obj, err := w.decodeObject(&node) if err != nil { - glog.Errorf("failure to decode api object: '%v' from %#v %#v", string(res.PrevNode.Value), res, res.PrevNode) + util.HandleError(fmt.Errorf("failure to decode api object: %v\nfrom %#v %#v", err, res, res.Node)) // TODO: expose an error through watch.Interface? // Ignore this value. If we stop the watch on a bad value, a client that uses // the resourceVersion to resume will never be able to get past a bad value. @@ -445,7 +446,7 @@ func (w *etcdWatcher) sendResult(res *etcd.Response) { case EtcdDelete, EtcdExpire: w.sendDelete(res) default: - glog.Errorf("unknown action: %v", res.Action) + util.HandleError(fmt.Errorf("unknown action: %v", res.Action)) } } diff --git a/pkg/storage/etcd/etcd_watcher_test.go b/pkg/storage/etcd/etcd_watcher_test.go index ad9e30a46cb..333b2efa00b 100644 --- a/pkg/storage/etcd/etcd_watcher_test.go +++ b/pkg/storage/etcd/etcd_watcher_test.go @@ -169,6 +169,7 @@ func TestWatchInterpretations(t *testing.T) { } func TestWatchInterpretation_ResponseNotSet(t *testing.T) { + _, codec := testScheme(t) w := newEtcdWatcher(false, nil, storage.Everything, codec, versioner, nil, &fakeEtcdCache{}) w.emit = func(e watch.Event) { t.Errorf("Unexpected emit: %v", e) @@ -181,6 +182,7 @@ func TestWatchInterpretation_ResponseNotSet(t *testing.T) { } func TestWatchInterpretation_ResponseNoNode(t *testing.T) { + _, codec := testScheme(t) actions := []string{"create", "set", "compareAndSwap", "delete"} for _, action := range actions { w := newEtcdWatcher(false, nil, storage.Everything, codec, versioner, nil, &fakeEtcdCache{}) @@ -195,6 +197,7 @@ func TestWatchInterpretation_ResponseNoNode(t *testing.T) { } func TestWatchInterpretation_ResponseBadData(t *testing.T) { + _, codec := testScheme(t) actions := []string{"create", "set", "compareAndSwap", "delete"} for _, action := range actions { w := newEtcdWatcher(false, nil, storage.Everything, codec, versioner, nil, &fakeEtcdCache{}) @@ -430,6 +433,7 @@ func TestWatchListIgnoresRootKey(t *testing.T) { } func TestWatchPurposefulShutdown(t *testing.T) { + _, codec := testScheme(t) server := etcdtesting.NewEtcdTestClientServer(t) defer server.Terminate(t) key := "/some/key" diff --git a/pkg/util/httpstream/spdy/roundtripper.go b/pkg/util/httpstream/spdy/roundtripper.go index 47b67307735..a054d30c233 100644 --- a/pkg/util/httpstream/spdy/roundtripper.go +++ b/pkg/util/httpstream/spdy/roundtripper.go @@ -212,7 +212,8 @@ func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connec if err != nil { responseError = "unable to read error from server response" } else { - if obj, err := api.Scheme.Decode(responseErrorBytes); err == nil { + // TODO: I don't belong here, I should be abstracted from this class + if obj, _, err := api.Codecs.UniversalDecoder().Decode(responseErrorBytes, nil, &unversioned.Status{}); err == nil { if status, ok := obj.(*unversioned.Status); ok { return nil, &apierrors.StatusError{ErrStatus: *status} } diff --git a/pkg/util/io/io.go b/pkg/util/io/io.go index 64bc074ba5f..d600c08c191 100644 --- a/pkg/util/io/io.go +++ b/pkg/util/io/io.go @@ -23,6 +23,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/runtime" ) // LoadPodFromFile will read, decode, and return a Pod from a file. @@ -39,7 +40,8 @@ func LoadPodFromFile(filePath string) (*api.Pod, error) { } pod := &api.Pod{} - if err := registered.GroupOrDie(api.GroupName).Codec.DecodeInto(podDef, pod); err != nil { + codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) + if err := runtime.DecodeInto(codec, podDef, pod); err != nil { return nil, fmt.Errorf("failed decoding file: %v", err) } return pod, nil @@ -50,7 +52,8 @@ func SavePodToFile(pod *api.Pod, filePath string, perm os.FileMode) error { if filePath == "" { return fmt.Errorf("file path not specified") } - data, err := registered.GroupOrDie(api.GroupName).Codec.Encode(pod) + codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) + data, err := runtime.Encode(codec, pod) if err != nil { return fmt.Errorf("failed encoding pod: %v", err) } diff --git a/pkg/util/io/io_test.go b/pkg/util/io/io_test.go index 993ff5fec57..466f436b8cb 100644 --- a/pkg/util/io/io_test.go +++ b/pkg/util/io/io_test.go @@ -24,6 +24,7 @@ import ( "github.com/pborman/uuid" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apimachinery/registered" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/io" "k8s.io/kubernetes/pkg/volume" ) @@ -32,8 +33,9 @@ func TestSavePodToFile(t *testing.T) { pod := volume.NewPersistentVolumeRecyclerPodTemplate() // sets all default values on a pod for equality comparison after decoding from file - encoded, err := registered.GroupOrDie(api.GroupName).Codec.Encode(pod) - registered.GroupOrDie(api.GroupName).Codec.DecodeInto(encoded, pod) + codec := api.Codecs.LegacyCodec(registered.GroupOrDie(api.GroupName).GroupVersion) + encoded, err := runtime.Encode(codec, pod) + runtime.DecodeInto(codec, encoded, pod) path := fmt.Sprintf("/tmp/kube-io-test-%s", uuid.New()) defer os.Remove(path) diff --git a/pkg/util/jsonpath/jsonpath.go b/pkg/util/jsonpath/jsonpath.go index 005e21d013c..35fcdd92d7a 100644 --- a/pkg/util/jsonpath/jsonpath.go +++ b/pkg/util/jsonpath/jsonpath.go @@ -205,7 +205,7 @@ func (j *JSONPath) evalIdentifier(input []reflect.Value, node *IdentifierNode) ( return results, fmt.Errorf("not in range, nothing to end") } default: - return input, fmt.Errorf("unrecongnized identifier %v", node.Name) + return input, fmt.Errorf("unrecognized identifier %v", node.Name) } return results, nil } @@ -216,7 +216,10 @@ func (j *JSONPath) evalArray(input []reflect.Value, node *ArrayNode) ([]reflect. for _, value := range input { value, isNil := template.Indirect(value) - if isNil || (value.Kind() != reflect.Array && value.Kind() != reflect.Slice) { + if isNil { + continue + } + if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { return input, fmt.Errorf("%v is not array or slice", value.Type()) } params := node.Params @@ -404,7 +407,7 @@ func (j *JSONPath) evalFilter(input []reflect.Value, node *FilterNode) ([]reflec value, _ = template.Indirect(value) if value.Kind() != reflect.Array && value.Kind() != reflect.Slice { - return input, fmt.Errorf("%v is not array or slice", value) + return input, fmt.Errorf("%v is not array or slice and cannot be filtered", value) } for i := 0; i < value.Len(); i++ { temp := []reflect.Value{value.Index(i)} diff --git a/pkg/util/jsonpath/jsonpath_test.go b/pkg/util/jsonpath/jsonpath_test.go index 0817dcf7198..01f07c908bb 100644 --- a/pkg/util/jsonpath/jsonpath_test.go +++ b/pkg/util/jsonpath/jsonpath_test.go @@ -93,7 +93,7 @@ func testFailJSONPath(tests []jsonpathTest, t *testing.T) { out = err.Error() } if out != test.expect { - t.Errorf("in %s, expect to get error %s, got %s", test.name, test.expect, out) + t.Errorf("in %s, expect to get error %q, got %q", test.name, test.expect, out) } } } @@ -160,7 +160,7 @@ func TestStructInput(t *testing.T) { testJSONPath(storeTests, t) failStoreTests := []jsonpathTest{ - {"invalid identfier", "{hello}", storeData, "unrecongnized identifier hello"}, + {"invalid identfier", "{hello}", storeData, "unrecognized identifier hello"}, {"nonexistent field", "{.hello}", storeData, "hello is not found"}, {"invalid array", "{.Labels[0]}", storeData, "map[string]int is not array or slice"}, {"invalid filter operator", "{.Book[?(@.Price<>10)]}", storeData, "unrecognized filter operator <>"}, diff --git a/pkg/util/yaml/decoder.go b/pkg/util/yaml/decoder.go index ebe597a598c..5846fc20ff4 100644 --- a/pkg/util/yaml/decoder.go +++ b/pkg/util/yaml/decoder.go @@ -136,7 +136,7 @@ func NewYAMLOrJSONDecoder(r io.Reader, bufferSize int) *YAMLOrJSONDecoder { // provide object, or returns an error. func (d *YAMLOrJSONDecoder) Decode(into interface{}) error { if d.decoder == nil { - buffer, isJSON := guessJSONStream(d.r, d.bufferSize) + buffer, isJSON := GuessJSONStream(d.r, d.bufferSize) if isJSON { glog.V(4).Infof("decoding stream as JSON") d.decoder = json.NewDecoder(buffer) @@ -148,10 +148,10 @@ func (d *YAMLOrJSONDecoder) Decode(into interface{}) error { return d.decoder.Decode(into) } -// guessJSONStream scans the provided reader up to size, looking +// GuessJSONStream scans the provided reader up to size, looking // for an open brace indicating this is JSON. It will return the // bufio.Reader it creates for the consumer. -func guessJSONStream(r io.Reader, size int) (io.Reader, bool) { +func GuessJSONStream(r io.Reader, size int) (io.Reader, bool) { buffer := bufio.NewReaderSize(r, size) b, _ := buffer.Peek(size) return buffer, hasJSONPrefix(b) diff --git a/pkg/util/yaml/decoder_test.go b/pkg/util/yaml/decoder_test.go index c4658a34008..e1e3f66095d 100644 --- a/pkg/util/yaml/decoder_test.go +++ b/pkg/util/yaml/decoder_test.go @@ -62,7 +62,7 @@ func TestSplitYAMLDocument(t *testing.T) { } func TestGuessJSON(t *testing.T) { - if r, isJSON := guessJSONStream(bytes.NewReader([]byte(" \n{}")), 100); !isJSON { + if r, isJSON := GuessJSONStream(bytes.NewReader([]byte(" \n{}")), 100); !isJSON { t.Fatalf("expected stream to be JSON") } else { b := make([]byte, 30) diff --git a/pkg/volume/aws_ebs/aws_ebs_test.go b/pkg/volume/aws_ebs/aws_ebs_test.go index c2b2921db62..736fd8e0d99 100644 --- a/pkg/volume/aws_ebs/aws_ebs_test.go +++ b/pkg/volume/aws_ebs/aws_ebs_test.go @@ -25,7 +25,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -259,11 +258,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) tmpDir, err := ioutil.TempDir(os.TempDir(), "awsebsTest") if err != nil { diff --git a/pkg/volume/fc/fc_test.go b/pkg/volume/fc/fc_test.go index 96531fb7456..21214f30034 100644 --- a/pkg/volume/fc/fc_test.go +++ b/pkg/volume/fc/fc_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -231,11 +230,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/gce_pd/gce_pd_test.go b/pkg/volume/gce_pd/gce_pd_test.go index 4e5651e393b..881ad3a3bd7 100644 --- a/pkg/volume/gce_pd/gce_pd_test.go +++ b/pkg/volume/gce_pd/gce_pd_test.go @@ -25,7 +25,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -274,11 +273,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) tmpDir, err := ioutil.TempDir(os.TempDir(), "gcepdTest") if err != nil { diff --git a/pkg/volume/glusterfs/glusterfs_test.go b/pkg/volume/glusterfs/glusterfs_test.go index f074e669009..bedcfb9a91d 100644 --- a/pkg/volume/glusterfs/glusterfs_test.go +++ b/pkg/volume/glusterfs/glusterfs_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/exec" @@ -193,12 +192,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }}, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - o.Add(ep) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim, ep) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/host_path/host_path_test.go b/pkg/volume/host_path/host_path_test.go index 4188fb05d6c..88623d2636c 100644 --- a/pkg/volume/host_path/host_path_test.go +++ b/pkg/volume/host_path/host_path_test.go @@ -26,7 +26,6 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util" @@ -256,11 +255,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/iscsi/iscsi_test.go b/pkg/volume/iscsi/iscsi_test.go index aec79aa3460..bc99b65922b 100644 --- a/pkg/volume/iscsi/iscsi_test.go +++ b/pkg/volume/iscsi/iscsi_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -231,11 +230,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/nfs/nfs_test.go b/pkg/volume/nfs/nfs_test.go index 8dd1414c44c..b9ebc48ac99 100644 --- a/pkg/volume/nfs/nfs_test.go +++ b/pkg/volume/nfs/nfs_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -233,11 +232,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(volume.VolumeConfig{}), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/volume/persistent_claim/persistent_claim_test.go b/pkg/volume/persistent_claim/persistent_claim_test.go index ea1c3cc6d6b..2a1c45cc3a9 100644 --- a/pkg/volume/persistent_claim/persistent_claim_test.go +++ b/pkg/volume/persistent_claim/persistent_claim_test.go @@ -234,11 +234,7 @@ func TestNewBuilder(t *testing.T) { } for _, item := range tests { - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(item.pv) - o.Add(item.claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, api.RESTMapper)) + client := testclient.NewSimpleFake(item.pv, item.claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(testProbeVolumePlugins(), newTestHost(t, client)) @@ -289,11 +285,7 @@ func TestNewBuilderClaimNotBound(t *testing.T) { ClaimName: "claimC", }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, api.RESTMapper)) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(testProbeVolumePlugins(), newTestHost(t, client)) diff --git a/pkg/volume/rbd/rbd_test.go b/pkg/volume/rbd/rbd_test.go index f9a6096ae19..d0c2b8997f4 100644 --- a/pkg/volume/rbd/rbd_test.go +++ b/pkg/volume/rbd/rbd_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/client/unversioned/testclient" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/mount" @@ -188,11 +187,7 @@ func TestPersistentClaimReadOnlyFlag(t *testing.T) { }, } - o := testclient.NewObjects(api.Scheme, api.Scheme) - o.Add(pv) - o.Add(claim) - client := &testclient.Fake{} - client.AddReactor("*", "*", testclient.ObjectReaction(o, testapi.Default.RESTMapper())) + client := testclient.NewSimpleFake(pv, claim) plugMgr := volume.VolumePluginMgr{} plugMgr.InitPlugins(ProbeVolumePlugins(), volume.NewFakeVolumeHost("/tmp/fake", client, nil)) diff --git a/pkg/watch/json/decoder.go b/pkg/watch/json/decoder.go index f6c0924dd40..d1632336f67 100644 --- a/pkg/watch/json/decoder.go +++ b/pkg/watch/json/decoder.go @@ -56,7 +56,7 @@ func (d *Decoder) Decode() (watch.EventType, runtime.Object, error) { return "", nil, fmt.Errorf("got invalid watch event type: %v", got.Type) } - obj, err := d.codec.Decode(got.Object.RawJSON) + obj, err := runtime.Decode(d.codec, got.Object.RawJSON) if err != nil { return "", nil, fmt.Errorf("unable to decode watch event: %v", err) } diff --git a/pkg/watch/json/decoder_test.go b/pkg/watch/json/decoder_test.go index 95312acb2e3..39bb892dfae 100644 --- a/pkg/watch/json/decoder_test.go +++ b/pkg/watch/json/decoder_test.go @@ -39,7 +39,7 @@ func TestDecoder(t *testing.T) { expect := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}} encoder := json.NewEncoder(in) go func() { - data, err := testapi.Default.Codec().Encode(expect) + data, err := runtime.Encode(testapi.Default.Codec(), expect) if err != nil { t.Fatalf("Unexpected error %v", err) } diff --git a/pkg/watch/json/encoder.go b/pkg/watch/json/encoder.go index 1110e9293fa..1eaf6a8a09c 100644 --- a/pkg/watch/json/encoder.go +++ b/pkg/watch/json/encoder.go @@ -30,11 +30,11 @@ import ( type Encoder struct { w io.Writer encoder *json.Encoder - codec runtime.Codec + codec runtime.Encoder } // NewEncoder creates an Encoder for the given writer and codec -func NewEncoder(w io.Writer, codec runtime.Codec) *Encoder { +func NewEncoder(w io.Writer, codec runtime.Encoder) *Encoder { return &Encoder{ w: w, encoder: json.NewEncoder(w), diff --git a/pkg/watch/json/types.go b/pkg/watch/json/types.go index f8fbd397aa8..28ee0dec75f 100644 --- a/pkg/watch/json/types.go +++ b/pkg/watch/json/types.go @@ -40,12 +40,12 @@ type WatchEvent struct { } // Object converts a watch.Event into an appropriately serializable JSON object -func Object(codec runtime.Codec, event *watch.Event) (interface{}, error) { +func Object(encoder runtime.Encoder, event *watch.Event) (interface{}, error) { obj, ok := event.Object.(runtime.Object) if !ok { return nil, fmt.Errorf("the event object cannot be safely converted to JSON: %v", reflect.TypeOf(event.Object).Name()) } - data, err := runtime.Encode(codec, obj) + data, err := runtime.Encode(encoder, obj) if err != nil { return nil, err } diff --git a/plugin/cmd/kube-scheduler/app/server.go b/plugin/cmd/kube-scheduler/app/server.go index fde563c0f06..07d85c60118 100644 --- a/plugin/cmd/kube-scheduler/app/server.go +++ b/plugin/cmd/kube-scheduler/app/server.go @@ -32,6 +32,7 @@ import ( client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/healthz" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/plugin/cmd/kube-scheduler/app/options" "k8s.io/kubernetes/plugin/pkg/scheduler" _ "k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider" @@ -152,19 +153,18 @@ func Run(s *options.SchedulerServer) error { } func createConfig(s *options.SchedulerServer, configFactory *factory.ConfigFactory) (*scheduler.Config, error) { - var policy schedulerapi.Policy - var configData []byte - if _, err := os.Stat(s.PolicyConfigFile); err == nil { - configData, err = ioutil.ReadFile(s.PolicyConfigFile) + var ( + policy schedulerapi.Policy + configData []byte + ) + configData, err := ioutil.ReadFile(s.PolicyConfigFile) if err != nil { - return nil, fmt.Errorf("Unable to read policy config: %v", err) + return nil, fmt.Errorf("unable to read policy config: %v", err) } - err = latestschedulerapi.Codec.DecodeInto(configData, &policy) - if err != nil { - return nil, fmt.Errorf("Invalid configuration: %v", err) + if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil { + return nil, fmt.Errorf("invalid configuration: %v", err) } - return configFactory.CreateFromConfig(policy) } diff --git a/plugin/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go b/plugin/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go index c6b39f29297..009244fd75b 100644 --- a/plugin/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go +++ b/plugin/pkg/scheduler/algorithmprovider/defaults/compatibility_test.go @@ -20,6 +20,7 @@ import ( "reflect" "testing" + "k8s.io/kubernetes/pkg/runtime" schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api" latestschedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api/latest" "k8s.io/kubernetes/plugin/pkg/scheduler/factory" @@ -92,16 +93,14 @@ func TestCompatibility_v1_Scheduler(t *testing.T) { for v, tc := range schedulerFiles { policy := schedulerapi.Policy{} - err := latestschedulerapi.Codec.DecodeInto([]byte(tc.JSON), &policy) - if err != nil { + if err := runtime.DecodeInto(latestschedulerapi.Codec, []byte(tc.JSON), &policy); err != nil { t.Errorf("%s: Error decoding: %v", v, err) continue } if !reflect.DeepEqual(policy, tc.ExpectedPolicy) { t.Errorf("%s: Expected:\n\t%#v\nGot:\n\t%#v", v, tc.ExpectedPolicy, policy) } - _, err = factory.NewConfigFactory(nil, "some-scheduler-name").CreateFromConfig(policy) - if err != nil { + if _, err := factory.NewConfigFactory(nil, "some-scheduler-name").CreateFromConfig(policy); err != nil { t.Errorf("%s: Error constructing: %v", v, err) continue } diff --git a/plugin/pkg/scheduler/api/latest/latest.go b/plugin/pkg/scheduler/api/latest/latest.go index cee44028f24..c01d91d32ed 100644 --- a/plugin/pkg/scheduler/api/latest/latest.go +++ b/plugin/pkg/scheduler/api/latest/latest.go @@ -17,7 +17,12 @@ limitations under the License. package latest import ( - "k8s.io/kubernetes/plugin/pkg/scheduler/api/v1" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer/json" + "k8s.io/kubernetes/pkg/runtime/serializer/versioning" + "k8s.io/kubernetes/plugin/pkg/scheduler/api" + _ "k8s.io/kubernetes/plugin/pkg/scheduler/api/v1" ) // Version is the string that represents the current external default version. @@ -33,6 +38,10 @@ const OldestVersion = "v1" var Versions = []string{"v1"} // Codec is the default codec for serializing input that should use -// the latest supported version. -// This codec can decode any object that Kubernetes is aware of. -var Codec = v1.Codec +// the latest supported version. It supports JSON by default. +var Codec = versioning.NewCodecForScheme( + api.Scheme, + json.NewSerializer(json.DefaultMetaFactory, api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), true), + []unversioned.GroupVersion{{Version: Version}}, + []unversioned.GroupVersion{{Version: runtime.APIVersionInternal}}, +) diff --git a/plugin/pkg/scheduler/api/register.go b/plugin/pkg/scheduler/api/register.go index 53a19cedafb..3b988f25bfa 100644 --- a/plugin/pkg/scheduler/api/register.go +++ b/plugin/pkg/scheduler/api/register.go @@ -26,9 +26,12 @@ var Scheme = runtime.NewScheme() // SchemeGroupVersion is group version used to register these objects // TODO this should be in the "scheduler" group -var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: ""} +var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: runtime.APIVersionInternal} func init() { + if err := Scheme.AddIgnoredConversionType(&unversioned.TypeMeta{}, &unversioned.TypeMeta{}); err != nil { + panic(err) + } Scheme.AddKnownTypes(SchemeGroupVersion, &Policy{}, ) diff --git a/plugin/pkg/scheduler/api/v1/register.go b/plugin/pkg/scheduler/api/v1/register.go index 110d1ec2604..8e6d754376c 100644 --- a/plugin/pkg/scheduler/api/v1/register.go +++ b/plugin/pkg/scheduler/api/v1/register.go @@ -18,7 +18,6 @@ package v1 import ( "k8s.io/kubernetes/pkg/api/unversioned" - "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/plugin/pkg/scheduler/api" ) @@ -26,9 +25,6 @@ import ( // TODO this should be in the "scheduler" group var SchemeGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"} -// Codec encodes internal objects to the v1 scheme -var Codec = runtime.CodecFor(api.Scheme, SchemeGroupVersion) - func init() { api.Scheme.AddKnownTypes(SchemeGroupVersion, &Policy{}, diff --git a/plugin/pkg/scheduler/factory/factory_test.go b/plugin/pkg/scheduler/factory/factory_test.go index 3f7654a4075..d98f60d03ea 100644 --- a/plugin/pkg/scheduler/factory/factory_test.go +++ b/plugin/pkg/scheduler/factory/factory_test.go @@ -87,8 +87,7 @@ func TestCreateFromConfig(t *testing.T) { {"name" : "PriorityOne", "weight" : 2}, {"name" : "PriorityTwo", "weight" : 1} ] }`) - err := latestschedulerapi.Codec.DecodeInto(configData, &policy) - if err != nil { + if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil { t.Errorf("Invalid configuration: %v", err) } @@ -111,8 +110,7 @@ func TestCreateFromEmptyConfig(t *testing.T) { factory := NewConfigFactory(client, api.DefaultSchedulerName) configData = []byte(`{}`) - err := latestschedulerapi.Codec.DecodeInto(configData, &policy) - if err != nil { + if err := runtime.DecodeInto(latestschedulerapi.Codec, configData, &policy); err != nil { t.Errorf("Invalid configuration: %v", err) } diff --git a/test/e2e/serviceloadbalancers.go b/test/e2e/serviceloadbalancers.go index 1db40bf9197..4b773c28e8b 100644 --- a/test/e2e/serviceloadbalancers.go +++ b/test/e2e/serviceloadbalancers.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/api" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/wait" utilyaml "k8s.io/kubernetes/pkg/util/yaml" @@ -272,7 +273,7 @@ func rcFromManifest(fileName string) *api.ReplicationController { json, err := utilyaml.ToJSON(data) Expect(err).NotTo(HaveOccurred()) - Expect(api.Scheme.DecodeInto(json, &controller)).NotTo(HaveOccurred()) + Expect(runtime.DecodeInto(api.Codecs.UniversalDecoder(), json, &controller)).NotTo(HaveOccurred()) return &controller } @@ -286,6 +287,6 @@ func svcFromManifest(fileName string) *api.Service { json, err := utilyaml.ToJSON(data) Expect(err).NotTo(HaveOccurred()) - Expect(api.Scheme.DecodeInto(json, &svc)).NotTo(HaveOccurred()) + Expect(runtime.DecodeInto(api.Codecs.UniversalDecoder(), json, &svc)).NotTo(HaveOccurred()) return &svc } diff --git a/test/integration/etcd_tools_test.go b/test/integration/etcd_tools_test.go index 720fe278d60..a8e8b0a9d64 100644 --- a/test/integration/etcd_tools_test.go +++ b/test/integration/etcd_tools_test.go @@ -49,7 +49,7 @@ func TestSet(t *testing.T) { if err != nil || resp.Node == nil { t.Fatalf("unexpected error: %v %v", err, resp) } - decoded, err := testapi.Default.Codec().Decode([]byte(resp.Node.Value)) + decoded, err := runtime.Decode(testapi.Default.Codec(), []byte(resp.Node.Value)) if err != nil { t.Fatalf("unexpected response: %#v", resp.Node) } @@ -67,7 +67,7 @@ func TestGet(t *testing.T) { ctx := context.TODO() framework.WithEtcdKey(func(key string) { testObject := api.ServiceAccount{ObjectMeta: api.ObjectMeta{Name: "foo"}} - coded, err := testapi.Default.Codec().Encode(&testObject) + coded, err := runtime.Encode(testapi.Default.Codec(), &testObject) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/test/integration/framework/master_utils.go b/test/integration/framework/master_utils.go index 73a9e96aa4a..5a7dab1f15f 100644 --- a/test/integration/framework/master_utils.go +++ b/test/integration/framework/master_utils.go @@ -21,7 +21,7 @@ import ( "net" "net/http" "net/http/httptest" - "runtime" + goruntime "runtime" "sync" "testing" "time" @@ -40,6 +40,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/master" + "k8s.io/kubernetes/pkg/runtime" etcdstorage "k8s.io/kubernetes/pkg/storage/etcd" "k8s.io/kubernetes/pkg/storage/etcd/etcdtest" "k8s.io/kubernetes/plugin/pkg/admission/admit" @@ -106,7 +107,7 @@ func NewMasterComponents(c *Config) *MasterComponents { // TODO: Support events once we can cleanly shutdown an event recorder. controllerManager.SetEventRecorder(&record.FakeRecorder{}) if c.StartReplicationManager { - go controllerManager.Run(runtime.NumCPU(), rcStopCh) + go controllerManager.Run(goruntime.NumCPU(), rcStopCh) } var once sync.Once return &MasterComponents{ @@ -157,6 +158,7 @@ func NewMasterConfig() *master.Config { APIGroupPrefix: "/apis", Authorizer: apiserver.NewAlwaysAllowAuthorizer(), AdmissionControl: admit.NewAlwaysAdmit(), + Serializer: api.Codecs, }, KubeletClient: kubeletclient.FakeKubeletClient{}, } @@ -195,7 +197,7 @@ func RCFromManifest(fileName string) *api.ReplicationController { glog.Fatalf("Unexpected error reading rc manifest %v", err) } var controller api.ReplicationController - if err := api.Scheme.DecodeInto(data, &controller); err != nil { + if err := runtime.DecodeInto(testapi.Default.Codec(), data, &controller); err != nil { glog.Fatalf("Unexpected error reading rc manifest %v", err) } return &controller diff --git a/test/integration/master_test.go b/test/integration/master_test.go index cfd95db08c6..9e74782ce69 100644 --- a/test/integration/master_test.go +++ b/test/integration/master_test.go @@ -19,9 +19,13 @@ limitations under the License. package integration import ( + "encoding/json" + "io/ioutil" "net/http" "testing" + "github.com/ghodss/yaml" + "k8s.io/kubernetes/test/integration/framework" ) @@ -51,3 +55,73 @@ func TestWatchSucceedsWithoutArgs(t *testing.T) { } resp.Body.Close() } + +func TestAccept(t *testing.T) { + _, s := framework.RunAMaster(t) + defer s.Close() + + resp, err := http.Get(s.URL + "/api/") + if err != nil { + t.Fatalf("unexpected error getting api: %v", err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("got status %v instead of 200 OK", resp.StatusCode) + } + + body, _ := ioutil.ReadAll(resp.Body) + if resp.Header.Get("Content-Type") != "application/json" { + t.Errorf("unexpected content: %s", body) + } + if err := json.Unmarshal(body, &map[string]interface{}{}); err != nil { + t.Fatal(err) + } + + req, err := http.NewRequest("GET", s.URL+"/api/", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Accept", "application/yaml") + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + body, _ = ioutil.ReadAll(resp.Body) + if resp.Header.Get("Content-Type") != "application/yaml" { + t.Errorf("unexpected content: %s", body) + } + t.Logf("body: %s", body) + if err := yaml.Unmarshal(body, &map[string]interface{}{}); err != nil { + t.Fatal(err) + } + + req, err = http.NewRequest("GET", s.URL+"/api/", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Accept", "application/json, application/yaml") + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + body, _ = ioutil.ReadAll(resp.Body) + if resp.Header.Get("Content-Type") != "application/json" { + t.Errorf("unexpected content: %s", body) + } + t.Logf("body: %s", body) + if err := yaml.Unmarshal(body, &map[string]interface{}{}); err != nil { + t.Fatal(err) + } + + req, err = http.NewRequest("GET", s.URL+"/api/", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Accept", "application") // not a valid media type + resp, err = http.DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != http.StatusNotAcceptable { + t.Errorf("unexpected error from the server") + } +} diff --git a/test/soak/serve_hostnames/serve_hostnames.go b/test/soak/serve_hostnames/serve_hostnames.go index 57b06661097..f19d42eb67f 100644 --- a/test/soak/serve_hostnames/serve_hostnames.go +++ b/test/soak/serve_hostnames/serve_hostnames.go @@ -35,6 +35,7 @@ import ( "k8s.io/kubernetes/pkg/api/unversioned" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/intstr" ) @@ -264,7 +265,7 @@ func main() { continue } var r unversioned.Status - if err := api.Scheme.DecodeInto(hostname, &r); err != nil { + if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), hostname, &r); err != nil { break } if r.Status == unversioned.StatusFailure {